내 연락처 정보
우편메소피아@프로톤메일.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
이전 장에서는 클래스와 객체에 대한 몇 가지 개념을 간략하게 언급했습니다. 이제 함께 살펴보겠습니다.
class는 정의 클래스 키워드이고 그 뒤에 정의 클래스의 이름이 옵니다. {}는 클래스의 본문이고 그 뒤에 ";"가 옵니다. 클래스에 정의된 멤버 함수는 기본적으로(인라인) 확장됩니다.
공개, 비공개, 보호의 세 가지 유형으로 구분되는 캡슐화 형식입니다. 액세스 권한의 범위는 액세스 한정자가 나타나는 위치부터 다음 액세스 한정자가 나타날 때까지 사용 도메인입니다. } 즉, class로 끝납니다.
클래스는 새 범위를 정의합니다. 클래스의 모든 멤버는 클래스 범위에 있습니다. 클래스 외부에서 멤버를 정의하는 경우 :: 범위 연산자를 사용하여 해당 멤버가 속한 클래스 도메인을 표시해야 합니다.
다음은 장편영화입니다 ///
• 클래스 유형을 사용하여 물리적 메모리에 객체를 생성하는 프로세스를 클래스 인스턴스화라고 합니다.
• 클래스는 객체에 대한 추상적인 설명입니다. 이는 클래스의 멤버 변수만 제한합니다.
이는 선언이며 공간이 할당되지 않습니다. 공간은 클래스를 사용하여 객체를 인스턴스화하는 경우에만 할당됩니다.
• 클래스는 여러 객체를 인스턴스화할 수 있으며, 인스턴스화된 객체는 실제 물리적 공간을 차지하고 클래스 멤버 변수를 저장합니다.
한 문장으로 요약하면: 객체가 클래스로 인스턴스화될 때만 공간이 할당됩니다. (그림으로 집을 짓는 것이 공간을 차지하는 것과 같습니다.)
- 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;
- };
위 코드에서는 private의 멤버 변수만 정의되고 새 공간이 열리지 않습니다.
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
그런 다음 두 개의 새로운 객체가 인스턴스화되어 새로운 공간이 열립니다.
• 첫 번째 멤버는 구조에서 주소 오프셋 0에 있습니다.
• 다른 멤버 변수는 특정 숫자(로그)의 정수 배수인 주소에 매핑되어야 합니다.
• 참고: 로그 = 컴파일러의 기본 로그와 멤버 크기 중 더 작은 값입니다.
• VS의 기본 로그는 8입니다.
• 구조의 전체 크기는 최대 쌍별 수(모든 변수 유형 중 가장 큰 값과 가장 작은 기본 쌍 매개변수)의 정수 배수입니다.
• 구조가 중첩된 경우 중첩된 구조는 최대 로그의 정수 배수 및 구조의 전체 크기에 맞춰 정렬됩니다.
이는 모든 가장 큰 로그(중첩 구조의 로그 포함)의 정수 배수입니다.
이것이 우리가 C 언어에서 알고 있는 것입니다. 다음 질문을 예로 들어보겠습니다.
- // 计算⼀下A实例化的对象是多⼤?
- class A
- {
- public:
- void Print()
- {
- cout << _ch << endl;
- }
- private:
- char _ch;
- int _i;
- };
호출되는 함수가 없으므로 멤버 변수만 살펴보십시오. _ch는 크기가 1바이트인 char 문자 유형이고, _i는 크기가 4바이트인 int 유형입니다. 정렬 번호이므로 _ 첨자가 0인 위치에 ch를 넣고 공백 3개를 비워둔 뒤 _i를 넣는다. 0부터 7까지 총 8바이트의 공간을 차지하는데, 이는 최대 정렬 번호의 정수배이다. . 조건을 충족하므로 이 질문에 대한 답은 8 입니다.
그리고 이 두 가지 질문이라면 어떨까요?
- class B
- {
- public:
- void Print()
- {
- //...
- }
- };
-
- class C
- {
- };
클래스 B에는 빈 함수가 있고 클래스 C에는 멤버가 없다는 것을 알 수 있습니다. C 언어에서는 두 클래스가 모두 1바이트의 공간을 차지하도록 만듭니다.
• 디ate 클래스에는 두 개의 멤버 함수인 Init과 Print가 있습니다. 그러면 d1이 Init과 Print를 호출할 때 서로 다른 객체 사이에 차이가 없습니다.
함수를 인쇄할 때 함수가 d1 객체에 액세스해야 하는지 d2 객체에 액세스해야 하는지 어떻게 알 수 있나요?그러면 여기서 C++가 제공하는 것을 볼 수 있습니다.
암시적인 이 포인터는 여기서 문제를 해결합니다.
• 컴파일러가 컴파일된 후 클래스의 멤버 함수는 기본적으로 이 포인터라고 하는 현재 클래스 유형의 포인터를 형식 매개 변수의 첫 번째 위치에 추가합니다. 예를 들어, Init of Date 클래스의 실제 프로토타입은 void Init(Date* const this, int year,
정수 월, 정수 일)
• 클래스의 멤버 함수에서 멤버 변수에 액세스할 때는 본질적으로 this 포인터를 통해 액세스합니다. 예를 들어 Init 함수에서 _year에 값을 할당하면 다음과 같습니다.
>_년 = 년도;
• C++에서는 이 포인터를 실제 매개변수와 형식 매개변수 위치에 명시적으로 작성할 수 없지만(컴파일러가 컴파일 중에 이를 처리함) 함수 본문에서 이 포인터를 명시적으로 사용할 수 있다고 규정합니다.
예를 들어
- class Date
- {
- public:
- // void Init(Date* const this, int year, int month, int day)
- void Init(int year, int month, int day)
- {
- // 编译报错:error C2106: “=”: 左操作数必须为左值
- // this = nullptr;
- // this->_year = year;
- _year = year;
- this->_month = month;
- this->_day = day;
- }
- void Print()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- // 这⾥只是声明,没有开空间
- int _year;
- int _month;
- int _day;
- };
-
- int main()
- {
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
- // d1.Init(&d1, 2024, 3, 31);this指针
- d1.Init(2024, 3, 31);
-
- // d1.Init(&d2, 2024, 7, 5);
- d2.Init(2024, 7, 5);
- //不能自己写
- return 0;
- }
Init 함수에 실제 매개변수를 쓰기 위해 C++는 함수의 주석에 표시된 대로 함수의 첫 번째 변수에 보이지 않는 상수(수정할 수 없는 이 포인터)를 추가합니다.
널 포인터가 역참조되지 않는 한 오류는 보고되지 않습니다.
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
p에 널 포인터가 할당되고 Print 함수를 가리키더라도 역참조되지 않으므로 여기에는 오류가 보고되지 않습니다.
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- cout << _a << endl;//这个地方多了一句
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
이전 프로그램과 비교하면 cout << _a << endl 문장이 하나 더 생겼는데 이때 널 포인터가 멤버 변수를 가리키고 널 포인터가 역참조되므로 프로그램에서 오류를 보고하게 됩니다.
기본 멤버 함수는 사용자가 명시적으로 구현하지 않고 컴파일러에 의해 자동으로 생성되는 멤버 함수입니다. 클래스의 경우 작성하지 않으면 컴파일러는 기본적으로 다음 6개의 기본 멤버 함수를 생성합니다.
생성자는 특별한 멤버 함수입니다. 생성자의 이름은 생성자라고 하지만 생성자의 주요 작업은 객체를 생성하기 위해 공간을 여는 것이 아닙니다(우리가 자주 사용하는 로컬 객체는 스택이 생성될 때 공간입니다). 프레임이 생성됨) ) 대신 개체가 인스턴스화될 때 개체가 초기화됩니다. 핵심은 초기화 Init 함수를 대체하는 것입니다.
1. 함수 이름은 클래스 이름과 동일합니다.
2. 반환 값이 없습니다. (void라고 쓸 필요는 없습니다)
3. 객체가 인스턴스화되면 시스템은 자동으로 해당 생성자를 호출합니다.
4. 생성자는 오버로드될 수 있습니다.
5. 클래스에 명시적으로 정의된 생성자가 없는 경우 C++ 컴파일러는 사용자가 이를 명시적으로 정의하면 더 이상 매개 변수가 없는 기본 생성자를 생성합니다.
처음 5개 항목은 비교적 간단합니다. 예를 들어보겠습니다.
- class Date
- {
- public:
- //1.⽆参构造函数,无需写void
- Date()//若不写,则构造函数会写出这一中无参构造函数
- {
- _year = 1;
- _month = 1;
- _day = 1;
- }
- //2.全缺省构造函数
- Date(int year = 1, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- private:
- int _year;
- int _month;
- int _day;
- }
-
- int main()
- {
- Date d1;//此时相当于已经自动调用了Init函数
- Date d2;
-
- return 0;
- }
프로그램에는 두 개의 생성자가 작성되는데, 하나는 매개변수 없는 생성자이고 다른 하나는 전체 기본 생성자입니다. 함수를 작성하지 않으면 프로그램은 앞서 언급한 매개변수 없는 생성자까지 하나까지 작성합니다. 두 함수는 호출 모호성으로 인해 오버로드된 함수이므로 두 함수가 동시에 존재할 수 없습니다.
6. 매개변수 없는 생성자, 전체 기본 생성자, 생성자를 작성하지 않을 때 컴파일러가 기본적으로 생성하는 생성자를 모두 기본 생성자라고 합니다. 그러나 이 세 가지 기능 중 하나만 존재하며 동시에 존재할 수는 없습니다. 매개변수 없는 생성자와 전체 기본 생성자는 함수 오버로드를 구성하지만 호출할 때 모호성이 있습니다. 많은 학생들이 기본 생성자를 컴파일러가 생성한 기본 생성자로 생각하고 있다는 점에 유의해야 합니다. 실제로 매개변수 없는 생성자와 전체 기본 생성자도 기본 생성자입니다. 매개변수를 기본 구성이라고 합니다.
여섯 번째 기능은 더 복잡합니다. 매개 변수가 없는 생성자, 모든 기본 생성자, 생성자를 작성하지 않을 때 컴파일러가 기본적으로 생성하는 생성자를 모두 기본 생성자라고 하며 기본 생성자를 이해하면 안 됩니다. 기본적으로 컴파일러에 의해 생성된 생성자로!
7. 작성하지 않으면 기본적으로 컴파일러에 의해 생성된 생성자는 내장형 멤버 변수의 초기화에 대한 요구 사항이 없습니다. 즉, 초기화 여부가 확실하지 않으며 컴파일러에 따라 다릅니다. . 사용자 정의 유형 멤버 변수의 경우 이 멤버 변수의 기본 생성자를 호출하여 초기화해야 합니다. 이 멤버 변수에 기본 생성자가 없으면 오류가 보고됩니다. 이 멤버 변수를 초기화하려면 초기화 목록을 사용하여 문제를 해결해야 합니다. 초기화 목록에 대해서는 다음 장에서 자세히 설명하겠습니다. .
소멸자의 기능은 생성자의 기능과 반대입니다. 소멸자는 객체 자체의 소멸을 완료하지 않습니다. 예를 들어 로컬 객체에는 함수가 종료되고 스택 프레임이 소멸됩니다. 우리는 이를 제어할 필요가 없습니다. C++에서는 객체가 소멸되면 객체의 리소스 정리 및 해제를 완료하기 위해 자동으로 소멸자가 호출됩니다. 소멸자의 기능은 이전에 Stack에서 구현한 Destroy 함수와 유사합니다. 예를 들어 Date에는 Destroy가 없습니다. 따라서 엄밀히 말하면 Date에는 소멸자가 필요하지 않습니다. 소멸자는 Destroy 함수와 마찬가지로 리소스를 적용하기 위해 스택 및 큐와 같은 로컬이 아닌 개체가 필요한 데이터 구조에 사용해야 합니다.
1. 소멸자 이름 앞에는 클래스 이름 앞에 ~ 문자가 옵니다. (C 언어의 비트 부정과 유사)
2. 매개변수도 없고 반환 값도 없습니다. (구조와 유사하므로 void를 추가할 필요는 없습니다.)
3. 클래스에는 소멸자가 하나만 있을 수 있습니다. 명시적으로 정의되지 않은 경우 시스템은 자동으로 기본 소멸자를 생성합니다.
4. 객체 수명주기가 끝나면 시스템은 자동으로 소멸자를 호출합니다.
5. 생성자와 유사하게 컴파일러에서 자동으로 생성된 소멸자를 작성하지 않으면 내장 유형 멤버가 처리되지 않습니다. 사용자 정의 유형 멤버는 해당 소멸자를 호출합니다.
6. 소멸자를 명시적으로 작성하면 사용자 정의 유형 멤버의 소멸자도 호출된다는 점에 유의해야 합니다. 즉, 상황에 관계없이 사용자 정의 유형 멤버의 소멸자가 자동으로 호출됩니다.
7. 클래스에 적용되는 리소스가 없으면 소멸자를 작성할 필요가 없으며, 기본적으로 생성된 소멸자를 사용할 수 있는 경우에는 Date 등 컴파일러에서 생성한 기본 소멸자를 그대로 사용할 수 있다. 표시할 필요는 없습니다. MyQueue와 같은 소멸자를 작성하지만 리소스 애플리케이션이 있는 경우 소멸자를 직접 작성해야 합니다. 그렇지 않으면 Stack과 같은 리소스 누출이 발생합니다.
8. 로컬 도메인에 있는 여러 개체의 경우 C++에서는 나중에 정의된 개체가 먼저 삭제되도록 규정합니다.
위의 생성자와 마찬가지로 C++에서 자동으로 생성된 기본 소멸자는 일반적으로 우리에게 큰 도움이 되지 않지만 대기열을 구현하기 위해 두 개의 스택을 사용하는 것과 같은 드문 경우에는 더 편리합니다.
- public:
- Stack(int n = 4)//相当于Init
- {
- _a = (STDataType*)malloc(sizeof(STDataType) * n);
- if (nullptr == _a)
- {
- perror("malloc fail!");
- return;
- }
- _capacity = n;
- _top = 0;
- }
- ~Stack()//相当于Destroy
- {
- cout << "~Stack()" << endl;
- free(_a);
- _a = nullptr;
- _top = _capacity = 0;
- }
- private:
- STDataType* _a;
- size_t _capacity;
- size_t _top;
- };
-
- class MyQueue
- {
- public:
- //编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源
- // 显⽰写析构,也会⾃动调⽤Stack的析构
-
- private:
- Stack pushst;
- Stack popst;
- };
이때 MyQueue의 컴파일러는 스택의 생성자(초기화)와 소멸자(소멸)를 자동으로 호출합니다.