[C++] 클래스 및 개체(1부)

목차

먼저 클래스 소개

둘째, 클래스의 정의

3. 클래스 액세스 한정자와 캡슐화

  1. 액세스 한정자

  2. 캡슐화

넷째, 수업의 범위

5. 클래스 인스턴스화

여섯, 클래스 개체 모델

  1. 클래스 개체의 크기 계산

  2. 클래스 객체의 저장 방법

  3. 구조/클래스 정렬 규칙

일곱, 이 포인터

  1. 이 포인터의 파생

  2. 이 포인터의 특징


먼저 C 및 C++에 대한 초기 이해에 대해 이야기하겠습니다.

C 언어는 프로세스 지향적 이며, 프로세스 에 집중하고 , 문제를 해결하기 위한 단계를 분석하고, 함수 호출을 통해 문제를 단계별로 해결합니다.

C++는 객체지향을 기반으로 객체 에 집중하고 , 한 가지를 다른 객체로 분할하고, 객체 간의 상호 작용에 의존하여 완성합니다.

먼저 클래스 소개

C 언어 구조 에서는 변수만 정의할 수 있는데 , C++ 에서는 변수뿐만 아니라 함수도 구조에 정의할 수 있습니다 . 예를 들어, 데이터 구조의 초기 단계에서  C 언어로 구현된 스택은 구조의 변수만 정의할 수 있지만 이제  C++로 구현되며 구조체에서도 함수를 정의할 수 있음을 알 수 있습니다 .

아래 코드와 같이 C와 C++의 차이점은 다음과 같습니다.

typedef int DataType;
struct Stack
{
	//成员函数
	void Init(size_t capacity)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(const DataType& data)
	{
		// 扩容
		_array[_size] = data;
		++_size;
	}
	DataType Top()
	{
		return _array[_size - 1];
	}
	void Destroy()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
	//成员变量
	DataType* _array;
	size_t _capacity;
	size_t _size;
};
int main()
{
	Stack s;
	s.Init(10);
	s.Push(1);
	s.Push(2);
	s.Push(3);
	cout << s.Top() << endl;
	s.Destroy();
	return 0;
}

위 구조의 정의는 C++에서 클래스로 대체하는 것이 바람직하다.

둘째, 클래스의 정의

class className
{
	// 类体:由成员函数和成员变量组成

}; // 一定要注意后面的分号

class 는 클래스를 정의하는 키워드 , ClassName클래스의 이름 , {}는 클래스의 몸체 , 클래스 정의 끝 뒤의 세미콜론은 생략할 수 없습니다.

클래스 본문의 내용을 클래스의 구성원 이라고 합니다. 클래스의 변수를 구성원 변수 라고 하고 , 클래스의 함수를 클래스의 구성원 함수 라고 합니다 .

클래스를 정의하는 방법에는 두 가지가 있습니다.

  • 선언과 정의는 모두 클래스 본문에 있습니다. 참고: 멤버 함수가 클래스에 정의된 경우 컴파일러는 이를 인라인 함수 로 처리할 수 있습니다 .

  • 클래스 선언은 .h 파일에 있고 멤버 함수 정의는 .cpp 파일에 있습니다. 참고: 멤버 함수 이름 앞에 클래스 이름을 추가해야 합니다.

여기서는 두 번째 방법을 선호합니다.

멤버 변수의 이름 지정 규칙 에 대한 제안 :

// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
	void Init(int year)
	{
		// 这里的year到底是成员变量,还是函数形参?
		year = year;
	}
private:
	int year;
};

위의 코드를 읽은 후 누가 멤버 변수이고 누가 함수 매개변수인지 약간 혼란스럽습니다. 따라서 다음 형식으로:

class Date
{
public:
	void Init(int year)
	{
		_year = year;
	}
private:
	int _year;
};

3. 클래스 액세스 한정자와 캡슐화

  1. 액세스 한정자

캡슐화를 달성하는 C++의 방법: 클래스를 사용하여 개체의 속성과 메서드를 결합하여 개체를 더 완전하게 만들고 액세스 권한을 통해 외부 사용자에게 선택적으로 인터페이스를 제공합니다.

 

[액세스 한정자 설명]

  1. 공용 수정 멤버는 클래스 외부에서 직접 액세스할 수 있습니다.
  2. 보호 및 개인 수정 멤버는 클래스 외부에서 직접 액세스할 수 없습니다(여기서 보호 및 개인은 유사합니다).
  3. 액세스 권한의 범위 는 이 액세스 한정자가 발생하는 시점부터 다음 액세스 한정자가 발생할 때까지입니다 .
  4. 뒤에 액세스 한정자가 없으면 범위는 }, 즉 클래스의 끝으로 이동합니다.
  5. 클래스의 기본 액세스 권한은 비공개이고 구조체는 공개입니다(구조체가 C와 호환되기 때문).

참고:  액세스 한정자는 데이터가 메모리에 매핑될 때 컴파일 시간에만 유용하며 액세스 한정자에는 차이가 없습니다.

  2. 캡슐화

  객체 지향의 세 가지 주요 특징: 캡슐화, 상속 및 다형성 .

  캡슐화: 데이터와 데이터 조작 방법을 유기적으로 결합하고 개체의 속성 및 구현 세부 정보를 숨기고 개체와 상호 작용할 수 있는 인터페이스만 노출합니다.

  캡슐화는 본질적으로 사용자가 클래스를 더 쉽게 사용할 수 있도록 하는 관리입니다.

  C++ 언어의 캡슐화는 클래스를 통해 데이터와 데이터 조작 방법을 유기적으로 결합하고, 액세스 권한을 통해 객체의 내부 구현 세부 정보를 숨기고, 클래스 외부에서 직접 사용할 수 있는 방법을 제어할 수 있습니다.

넷째, 수업의 범위

클래스는 새로운 범위를 정의 하고 클래스의 모든 구성원은 클래스의 범위에 있습니다. 클래스 외부에서 멤버를 정의할 때 :: 범위 연산자를 사용하여 멤버가 속한 클래스 도메인을 나타내야 합니다.

class Person
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int  _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
	cout << _name << " " << _gender << " " << _age << endl;
}

5. 클래스 인스턴스화

    클래스 유형으로 객체를 생성하는 프로세스를 클래스의 인스턴스화라고 합니다.

  1. 클래스는 객체에 대한 설명으로 클래스가 어떤 멤버로 구성되어 있는지 정의하고 이를 저장할 실제 메모리 공간을 할당하지 않고 클래스를 정의하는 모델과 같은 것입니다. 특정 학생 정보를 설명하는 클래스라고 생각하면 됩니다.
  2. 클래스는 여러 개체를 인스턴스화할 수 있으며 인스턴스화된 개체는 실제 물리적 공간을 차지하고 클래스 멤버 변수를 저장합니다.
  3. 클래스에서 객체를 인스턴스화하는 것은 실제로 건축 설계 도면을 사용하여 집을 짓는 것과 같으며 객체는 실제로 데이터를 저장하고 물리적 공간을 차지할 수 있습니다.

인스턴스화된 객체만이 실제로 데이터를 저장하고 물리적 공간을 차지할 수 있습니다.

class Person
{
public:
	void PrintInfo();
public:
	char* _name;
	char* _sex;
	char* _age;
};
void Test()
{
	Person man;
	man._name = "xxxx";
	man._sex = "男";
	man._age = 10;
	man.PrintInfo();
}

여섯, 클래스 개체 모델

  1. 클래스 개체의 크기 계산

#include<iostream>
using namespace std;

class Person
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;
};
void Person::PrintPersonInfo()
{
	cout << _name << " " << _gender << " " << endl << _age << endl;
}

int main()
{
	Person man;
	cout << sizeof(man) << endl;
	return 0;
}

작업 결과:

  2. 클래스 객체의 저장 방법

#include<iostream>
using namespace std;

//1.类中既有成员变量,又有成员函数
class A1
{
public:
    void f1() {}
private:
    int _a;
};
//2.类中仅有成员函数
class A2
{
public:
    void f2() {}
};
//3.类中仅有成员变量
class A3
{
private:
    int _a;
};
//4.空类
class A4
{};
int main()
{
    cout << "A1: " << sizeof(A1) << " A2: " << sizeof(A2) << " A3: " << sizeof(A3) << " A4: " << sizeof(A4) << endl;
    return 0;
}

 작업 결과:

  결론: 클래스의 크기는 실제로 클래스에 있는 "멤버 변수"의 합입니다.물론 메모리 정렬에 주의를 기울여야 합니다.

  참고: 빈 클래스의 크기, 빈 클래스는 특별합니다. 컴파일러는 이 클래스의 개체를 고유하게 식별하기 위해 빈 클래스에 바이트를 제공합니다.

  3. 구조/클래스 정렬 규칙

  클래스의 메모리 정렬 규칙은 구조체와 동일하며 이전에 작성한 블로그를 참조할 수 있습니다.

  사용자 지정 유형에 대한 자세한 설명(구조, 비트 필드)

일곱, 이 포인터

  1. 이 포인터의 파생

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;     // 年
	int _month;    // 月
	int _day;      // 日
};
int main()
{
	Date d1, d2;
	d1.Init(2022, 6, 19);
	d2.Init(2023, 6, 19);
	d1.Print();
	d2.Print();
	return 0;
}

  Date 클래스에는 Init과 Print 두 개의 멤버 함수가 있고 함수 본문에 개체가 구분되어 있지 않은 경우 d1이 Init 함수를 호출할 때 d2 대신 d1 개체를 설정해야 함을 함수는 어떻게 알 수 있습니까? 물체?

  C++는 this 포인터를 도입하여 이 문제를 해결합니다. 즉, C++ 컴파일러는 각 "비정적 멤버 함수"에 숨겨진 포인터 매개 변수를 추가하여 포인터가 현재 개체(함수가 호출될 때 함수를 호출하는 개체)를 가리키도록 합니다. 실행 중), 함수 본문의 모든 "구성원 변수" 작업은 이 포인터를 통해 액세스됩니다. 모든 작업이 사용자에게 투명하다는 것입니다. 즉, 사용자가 전달할 필요가 없으며 컴파일러가 자동으로 완료합니다.

  2. 이 포인터의 특징

  1. 이 포인터의 유형: 클래스 유형 * const, 즉 멤버 함수에서 이 포인터에 값을 할당할 수 없습니다.
  2. "구성원 함수" 내에서만 사용할 수 있습니다.
  3. this 포인터는 본질적으로 "구성원 함수"의 형식 매개변수입니다. 개체가 구성원 함수를 호출하면 개체 주소가 이 형식 매개변수에 실제 매개변수로 전달됩니다. 따라서 this 포인터는 개체에 저장되지 않습니다.
  4. this 포인터는 "멤버 함수"의 첫 번째 암시적 포인터 매개 변수입니다. 일반적으로 ecx 레지스터를 통해 컴파일러에 의해 자동으로 전달되며 사용자가 전달할 필요가 없습니다.

이 포인터는 어디에 있습니까?

대답: 이것은 공식 매개변수로 존재하며 일반적으로 스택에 있으며 일부 컴파일러는 레지스터에 넣습니다.

보충 질문:

다음 코드를 분석합니다.

class A
{
public:
    void Print()
    {
        cout << "Print()" << endl;
    }
    void PrintA()
    {
        cout << _a << endl;
    }
private:
    int _a;
};
int main()
{
    A* p = nullptr;
    p->Print();    // 正常运行
    p->PrintA();   // 运行崩溃
    return 0;
}

왜 이런 일이 발생합니까?

대답: Print() 멤버 함수를 호출하고 Print()가 p가 가리키는 개체에 존재하지 않으면 컴파일러는 p를 역참조하지 않으며 동시에 Print()는 this 포인터를 사용하여 p에 액세스하지 않습니다. 공백이 가리키는 데이터 이러한 이유로 컴파일러는 오류를 보고하지 않고 Print() 함수를 정상적으로 실행합니다.(*p).PrintfA()도 마찬가지입니다. ) 그것이 가리키는 공간의 데이터에 액세스하기 위해 nullptr의 역참조로 인해 프로그램이 자연스럽게 충돌합니다!


이 기사에 부족한 점이 있으면 아래에 의견을 보내 주시면 최대한 빨리 수정하겠습니다.

   오래된 아이언, 좋아하고 주목하는 것을 잊지 마십시오!!!  

추천

출처blog.csdn.net/m0_63198468/article/details/131284922