[C++ 에센스샵] 4. C++ 클래스와 객체(on) 객체지향, 클래스, 이포인터

목차

1. 프로세스 지향과 객체 지향

2. 수업 소개

3. 클래스 정의

 4. 클래스 접근 한정자와 캡슐화

4.1 클래스 접근 한정자

4.2 포장

 5. 클래스 범위

 6. 클래스 인스턴스화

7. 클래스 객체 모델

7.1 클래스 객체의 저장 방법

7.2 학급 규모

7.2.1 빈 클래스의 크기

 7.2.2 구조 메모리 정렬 규칙

8. 해당 키워드에 대한 심층 설명

 8.1 이 포인터의 파생

 8.2 이 포인터의 특성

 9. 널 포인터로 멤버 함수를 호출할 때의 문제


1. 프로세스 지향과 객체 지향

        C 언어는 문제 해결 프로세스를 지향하고 함수 호출을 통해 문제를 차례로 해결하는 프로세스 지향 언어입니다.

예를 들어, 빨래를 하면: 대야 - 물 넣기 - 옷 넣기 - 세제 넣기...  

        C++는 객체 지향을 기반으로 하지만 객체에 중점을 둡니다. 세탁을 예로 들면 C++는 사람, 옷, 세제 등에 중점을 둡니다.  

2. 수업 소개

        C 언어에서는 구조체가 변수만 정의할 수 있지만, C++에서는 구조체가 클래스로 업그레이드되어 변수 정의뿐만 아니라 함수 정의도 가능해진다.

struct Date
{
	void addDate(int x) {}
	int _year;
	int _month;
	int _day;
};

      그러나 위의 구조는 C++에서 정의하기 위해 클래스를 사용하는 것이 더 좋습니다.

3. 클래스 정의

        class는 클래스를 정의하는 키워드이고, {}의 콘텐츠는 클래스의 클래스 본문, 내부에 정의된 콘텐츠는 멤버, 클래스의 변수는 클래스의 속성 또는 클래스의 멤버 변수라고 합니다. , ClassName은 클래스의 이름이며, 중괄호 뒤의 세미콜론 ";"은 생략할 수 없습니다.

class ClassName
{     //类体};

        클래스는 두 가지 방법으로 정의할 수 있습니다. 

  1. 선언과 정의는 모두 클래스 본문에 배치됩니다. (멤버 함수는 클래스 본문에 정의되며 컴파일러는 이를 인라인 함수로 처리할 수 있습니다.)
    class Date 
    {
    public:
    	void addDate(int x)
    	{
    		//
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
  2. 클래스 선언은 .h 파일에 위치하며, 멤버 함수는 .cpp 파일에 정의됩니다(클래스 이름::은 멤버 함수 앞에 추가되어야 함 ).
    #pragma once
    class Date
    {
    public:
    	void addDate(int x);
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    
    #include<iostream>
    #include"date.h"
    void Date::addDate(int x)
    {
    	std::cout << "void addDate(int x);";
    }

     4. 클래스 접근 한정자와 캡슐화

4.1 클래스 접근 한정자

        

        또한  클래스  의 기본 액세스 권한은  private 이고 구조체 의 기본 액세스 권한은  public 입니다 (c 언어와 호환 가능).

4.2 포장

        객체지향에는 캡슐화, 상속, 다형성이라는 세 가지 특성이 있습니다 . 나머지 두 가지에 대해서는 후속 기사에서 논의할 것입니다. 오늘 이야기할 주요 내용은 캡슐화입니다. 캡슐화란 무엇입니까? 데이터와 데이터 조작 방법을 유기적으로 결합하고 개체 속성과 구현 세부 정보를 숨깁니다. 개체와 상호 작용할 인터페이스만 노출합니다.

        캡슐화는 본질적으로 일종의 데이터 관리입니다. 마치 은행이 은행 내부 처리 자금의 세부 사항을 우리에게 공개하지 않고 사용자와 소통할 수 있는 창을 하나 이상만 열어서 사용 비용을 줄이는 것과 같습니다. 포장도 마찬가지다.

 5. 클래스 범위

        클래스는 우리에게 새로운 범위를 재정의합니다. 클래스의 모든 멤버는 클래스 범위에 있습니다. 클래스 외부에서 멤버를 정의하려면 "::" 범위 연산자를 사용하여 해당 멤버가 속한 클래스 도메인을 나타내야 합니다.

class Date 
{
public:
	void addDate();
private:
	int _year;
	int _month;
	int _day;
};
void Date::addDate()
{
	cout << "void addDate()";
}

 6. 클래스 인스턴스화

         클래스의 인스턴스화는 클래스를 사용하여 객체를 생성하는 과정이며 클래스는 드로잉과 동일하며 인스턴스화는 드로잉을 구현하는 프로세스입니다. 클래스는 여러 개체를 인스턴스화할 수 있으며 인스턴스화된 개체는 실제 물리적 공간을 차지하고 클래스 멤버 변수를 저장합니다.

 

7. 클래스 객체 모델

7.1 클래스 객체의 저장 방법

        클래스가 인스턴스화되면 멤버 변수만 저장되고, 멤버 함수는 공개 코드 세그먼트에 저장됩니다.

         객체의 크기를 얻어 위의 저장 방법을 확인할 수도 있습니다.

class Date 
{
public:
	void addDate()
	{}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	cout << sizeof(d1);
}

 산출:

12

        코드를 보면 클래스의 크기가 int형 변수 3개의 크기에 불과하다는 것을 알 수 있으므로 저장 모델이 정확하다는 것을 증명할 수 있습니다. 그리고 우리 학급의 규모도 메모리 정렬 규칙을 따릅니다. 

7.2 학급 규모

7.2.1 빈 클래스의 크기

        빈 클래스를 정의하고 이 클래스를 인스턴스화하면 인스턴스화된 객체의 크기는 0이 아닌 1이 됩니다. 이 바이트는 객체가 존재함을 나타내는 위치를 차지하는 데 사용됩니다. 비어 있지 않은 클래스의 크기는 메모리 정렬 규칙에 따라 계산되어야 합니다.

class MyClass
{};
int main()
{
	MyClass mc;
	cout << sizeof(mc);
}

산출:

1

 7.2.2 구조 메모리 정렬 규칙

         C++의 구조체와 클래스 모두 메모리 정렬의 원리를 따르는데, 여기서의 메모리 정렬은 C언어의 구조체 메모리 정렬과 완전히 동일하므로 자세한 설명은 생략하겠습니다. 

  1. 첫 번째 멤버는 구조의 오프셋 0에 있습니다.
  2. 다른 멤버 변수는 특정 숫자(정렬 번호)의 정수배인 주소에 정렬되어야 합니다. 참고: 정렬 = 컴파일러의 기본 정렬과 멤버 크기 사이의 더 작은 값입니다. VS의 기본 정렬 번호는 8입니다.
  3. 구조의 전체 크기는 최대 정렬 수(모든 변수 유형 중 가장 큰 값과 가장 작은 기본 정렬 매개변수)의 정수 배수입니다.
  4. 구조가 중첩된 경우 중첩된 구조는 자체 최대 정렬의 정수 배수로 정렬되고 구조의 전체 크기는 모든 최대 정렬(중첩 구조의 정렬 포함)의 정수 배수입니다.

8. 해당 키워드에 대한 심층 설명

 8.1 이 포인터의 파생

        위의 설명에서 우리는 멤버 함수가 공개 코드 영역에 저장된다는 것을 알고 있는데, 인스턴스화된 객체가 어떻게 멤버 함수에서 자신의 멤버 변수를 정확하게 호출할 수 있을까요? 이것이 우리가 말하는 this 키워드이며 실제로는 this 포인터입니다. this 포인터는 실제로 멤버 함수의 첫 번째 암시적 매개변수 입니다 . 인스턴스화하는 객체가 이 함수를 호출하면 자동으로 자체 주소를 전달합니다.

        우리가 보는 코드와 출력은 다음과 같습니다.

class Date
{
public:
	void print()
	{
		cout << _year << "年" << _month << "月" << _day << "日";
	}
private:
	int _year = 2023;  //缺省值,用于默认构造时成员变量的默认值,会在后续构造函数中讲到
	int _month = 8;    //暂时不用太过注意
	int _day = 6;
};
int main()
{
	Date d1;
	d1.print();
}

출력:
2023년 8월 6일

        실제 코드(컴파일러에 의해 처리됨):

 8.2 이 포인터의 특성

  1. 이 포인터의 유형: * const 유형, 즉 멤버 함수에서 this 포인터에 값을 할당할 수 없습니다.
  2. "멤버 함수" 내에서만 사용할 수 있습니다.
  3. this 포인터는 본질적으로 "멤버 함수"의 형식 매개변수입니다. 개체가 멤버 함수를 호출하면 개체 주소가 이 형식 매개변수에 실제 매개변수로 전달됩니다. 따라서 this 포인터는 객체에 저장되지 않습니다.
  4. this 포인터는 "멤버 함수"의 첫 번째 암시적 포인터 매개변수입니다. 일반적으로 컴파일러에 의해 ecx 레지스터를 통해 자동으로 전달되며 사용자가 전달할 필요가 없습니다.
//相当于下面的代码
class Date
{
public:
	void print(Date* const this)
	{
		cout << this->_year << "年" << this->_month << "月" << this->_day << "日";
	}
private:
	int _year = 2023;  
	int _month = 8;    
	int _day = 6;
};
int main()
{
	Date d1;
	d1.print(&d1);
}

         그러나 인쇄 함수 호출에 실패하게 만드는 또 다른 특별한 상황이 있습니다. 즉, 우리가 전달하는 객체 d1은 다음과 같이 const 유형입니다.

 9. 널 포인터로 멤버 함수를 호출할 때의 문제

        여기에서 모두가 의구심을 가질 수 있습니다. 널 포인터가 어떻게 멤버 함수를 호출할 수 있습니까? 대답은 함수의 멤버 변수에 대한 역참조 작업이 없을 때 호출될 수 있다는 것입니다. 다음과 같이:

        여기의 인쇄 함수는 공개 코드 영역에 저장되어 있으며 널 포인터 액세스 동작이 없으므로 성공적으로 호출할 수 있습니다.

class Date
{
public:
	void print()
	{
		//这里没有在函数体内对成员函数进行操作,
        //而我们的成员函数是统一放在公共代码区的,
        //所以这里也就没有发生任何的空指针行为,所以是可以被调用成功的。
		cout << "void print()" << endl;
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 6;
};
int main()
{
	Date* d1 = nullptr;
	d1->print();
}

 산출:

무효 인쇄()

        다음 인쇄 함수는 널 포인터를 역참조하므로 프로그램이 충돌합니다.

class Date
{
public:
	void print()
	{
		//这里对空指针进行了解引用的操作,
		//引发了空指针的非法访问,所以代码就会直接崩溃
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
		//引发了异常: 读取访问权限冲突。this 是 nullptr。
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 6;
};
int main()
{
	Date* d1 = nullptr;
	d1->print();
}

출력: 출력 없음

예외: 예외가 발생했습니다: 읽기 액세스 위반입니다. 이것은 nullptr입니다.

요약하자면, 우리는 클래스와 관련된 기본 지식을 거의 다뤘고, 나중에 클래스의 6가지 기본 멤버 함수에 대해 설명하기 시작할 것입니다. 이는 이해하기 어려울 것이므로 계속 지켜봐 주시기 바랍니다. 코드 텍스트는 쉽지 않습니다. 3회 연속 올림픽을 기억하세요.

추천

출처blog.csdn.net/qq_64293926/article/details/132135399