目录
(另-先贴一份Date函数在这里,往后篇幅经常用到噢!!!)
(另-先贴一份Stack函数和Queue函数在这里,往后篇幅经常用到噢!!!)
多多重复,百炼成钢!!!
class-类的关键字
classname-类的名字
{-}内,类的主体,后紧接着分号;
类体中的内容称为类的成员;类内变量称为类的属性或者成员变量;类内函数成为类的方法或者成员函数。



另: class的默认访问权限为private,struct为public(因为struct要兼容C)
访问限定符只在 编译 时有用,当数据映射到内存后,没有任何访问限定符上的区别
- //类的作用域-类定义了一个新的作用域,类的所有成员都在类的作用域中。
- //在类体外定义成员时,需要使用 ::
-
- class people
- {public:
- int findperson();
- private:
- int son;
- int dad;
- int mother;
- };
- //作用域操作符指明成员属于哪个类域。
- int people::findperson()//这里的的findperson()属于people这个类域
- {
- return people::mother;
- }
空类:编译器给了一个字节- 腾出空间来给该类标识(没料的菜鸟也可以占位!!!)
只有一个成员函数:编译器也给了一个字节
有成员变量时:要按照结构体内存对齐规则!!!
1. 第一个成员在与结构体偏移量为0的地址处。2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的对齐数为83. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。


- 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, 9, 23);
- d2.Init(2022, 1, 11);
- d1.Print();
- d2.Print();
- return 0;
- }
1. this 指针的类型:类类型 * const ,即成员函数中,不能给 this 指针赋值。2. 只能在 “ 成员函数 ” 的内部使用3. this 指针本质上是 “ 成员函数 ” 的形参 ,当对象调用成员函数时(创建栈帧),将对象地址作为实参传递给 this形参。所以 对象中不存储 this 指针 。4. this 指针是 “ 成员函数 ” 第一个隐含的指针形参,一般情况由编译器通过 ecx 寄存器自动传递,不需要用户传递
根据以下代码:
我们创建了一个Date类
定义了一个d1和d2并且都初始化
1.d1和d2调用的是同一个Print()函数-在公共区
2.c++编译器通过给每(非静态成员函数)一个this指针(隐藏的),让该指针指向当前对象(调用公共区函数的对象)-把对象的地址传给函数
class Date { public: void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } void Print()// 等同于Print(Date*this)-(Date*d1)/(Date*d2) { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1; Date d2; d1.Init(1988, 11, 29); d1.Print(); d2.Init(1997, 10, 8); d2.Print(); return 0; }
- typedef int DataType;
- class Stack
- {
- public:
-
- Stack()
- {
- /* cout << "Queue q调用了自定义类型 Stack的构造函数来初始化" << endl;*/
- _array = (DataType*)malloc(sizeof(DataType) * 3);
- if (NULL == _array)
- {
- perror("malloc申请空间失败!!!");
- return;
- }
- _capacity = 3;
- _size = 0;
- }
- ~Stack()
- {
- cout << "Queue q调用Stack的析构函数" << endl;
- free(_array);
- _array = NULL;
- _capacity = 0;
- _size = 0;
- }
- void Push(DataType data)
- {
- CheckCapacity();
- _array[_size] = data;
- _size++;
- }
- void Pop()
- {
- if (Empty())
- return;
- _size--;
- }
- DataType Top() { return _array[_size - 1]; }
- int Empty() { return 0 == _size; }
- int Size() { return _size; }
-
- private:
- void CheckCapacity()
- {
- if (_size == _capacity)
- {
- int newcapacity = _capacity * 2;
- DataType* temp = (DataType*)realloc(_array, newcapacity *
- sizeof(DataType));
- if (temp == NULL)
- {
- perror("realloc申请空间失败!!!");
- return;
- }
- _array = temp;
- _capacity = newcapacity;
- }
- }
- private:
- DataType* _array;
- int _capacity;
- int _size;
- };
- class MyQueue {
- public:
- void push(int x)
- {
- _pushST.Push(x);
- }
-
- Stack _pushST;
- Stack _popST;
- private:
- int _a;
- int _b;
- //size_t _size;
- };
- int main()
- {
- MyQueue q;
- q.push(1);
- q.push(2);
-
-
- /*s.Destroy();*/
- return 0;
- }
如果一个类中什么成员都没有,简称为空类。空类中真的什么都没有吗???并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员 函数。默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

是否会因为每次忘记初始化对象而烦恼?现在它来了!构造函数自动帮你初始化对象!
1. 函数名与类名相同;创建类类型对象时由编译器自动调用,以保证 每个数据成员都有一个 合适的初始值,并且在对象整个生命周期内只调用一次。2. 无返回值。3. 对象实例化时编译器自动调用对应的构造函数。![]()
4. 构造函数可以重载
5.构造函数可以缺省
1.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦 用户显式定义编译器将不再生成。2.C++编译器把类型分成 内置类型 (语言提供的数据类比如int、char、double等等)和 自定义类型 (我们构造的class、struct、union等等)。3.C++编译器对 内置类型 不初始化(给随机值),对 自定义类型 会调用该自定义类型的构造函数。我们可以看到c++编译器对内置类型不初始化(给随机值)![]()
然而:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁
即:内置类型成员变量在类中声明时可以给默认值-给的是缺省值
在这里我们可以看到c++编译器对自定义类型会调用该函数的构造函数来初始化
无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。另:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。若Queue q调用Stack的默认构造函数时,此时Stack没有无参数构造函数、全缺省构造函数,则会调用编译器默认生成的构造函数(里面给的内置参数为随机值)。解决办法:在内置参数处设置缺省值(初始化)。![]()
typedef int DataType; class Stack { public: //Stack() //{ ///* cout << "Queue q调用了自定义类型 Stack的构造函数来初始化" << endl;*/ // _array = (DataType*)malloc(sizeof(DataType) * 3); // if (NULL == _array) // { // perror("malloc申请空间失败!!!"); // return; // } // _capacity = 3; // _size = 0; //} //~Stack() //{ // cout << "Queue q调用Stack的析构函数" << endl; // free(_array); // _array = NULL; // _capacity = 0; // _size = 0; //} void Push(DataType data) { CheckCapacity(); _array[_size] = data; _size++; } void Pop() { if (Empty()) return; _size--; } DataType Top() { return _array[_size - 1]; } int Empty() { return 0 == _size; } int Size() { return _size; } private: void CheckCapacity() { if (_size == _capacity) { int newcapacity = _capacity * 2; DataType* temp = (DataType*)realloc(_array, newcapacity * sizeof(DataType)); if (temp == NULL) { perror("realloc申请空间失败!!!"); return; } _array = temp; _capacity = newcapacity; } } private: DataType* _array; int _capacity=1; int _size=1; }; class MyQueue { public: void push(int x) { cout << "调用默认Stack构造函数完成push" << endl; _pushST.Push(x); } Stack _pushST; Stack _popST; private: int _a = 0; int _b = 0; //size_t _size; }; int main() { MyQueue q; q.push(1); /*s.Destroy();*/ return 0; }
是否常常会因为忘记销毁对象而烦恼?是否会因为总是要在结尾处把对象free掉而烦恼?
不用担心!现在它来了!析构函数自动帮你销毁对象!
1. 析构函数名是在类名前加上字符 ~。2. 无参数无返回值类型。3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。(另:有三种销毁对象情况:第一种是局部变量->在局部函数(栈帧)销毁时一齐销毁第二种是全局变量->在全局函数结束时销毁第三种是类malloc函数->要主动去free销毁 (是这种情况时最好写析构函数)
下面的程序我们会看到,编译器 生成的默认析构函数,对自定类型成员调用它的析构函数。![]()
1.只有单个形参,该形参是对本类类型对象的 引用 (一般常用 const 修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。2. 拷贝构造函数是构造函数的一个 重载 形式。3.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。4.拷贝函数使用场景:使用已存在对象构建新对象、返回值类型为已存在类对象、函数参数类型为已存在类对象![]()
class Date { public: Date(int year=1, int month=1, int day=1)//构造函数-自动调用 { cout << "调用构造函数" << endl; _year = year; _month = month; _day = day; } ~Date() { cout << "自动调用析构函数" << endl; _year = 0; _month = 0; _day = 0; } Date(const Date& d)//拷贝构造函数 { cout<< "调用拷贝构造" << endl; _year = d._year; _month = d._month; _day = d._day; } void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1(1988, 3, 23); Date d2(d1); d2.Print(); return 0; }
若未显式定义,编译器会生成默认的拷贝构造函数。1.默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。2.若类中涉及到要资源申请的-必须要写拷贝构造函数(如:链表、二叉树、栈、队列等等)总结成两句话:需要写析构函数的类-就需要写拷贝构造函数;(比如队列调用栈、栈调用队列等等)不需要写析构函数的类-使用编译器默认生成的拷贝构造就🆗![]()
浅拷贝可能引发的问题:
(1)同一块资源被释放两次;
(2)后一个类通过拷贝构造函数在同一块空间改变前一个类(打个比方:st2通过st1前往公园的路反向找到st1的家并且。。。细想是不是很恐怖!)
所以我们要写拷贝构造函数让st2也拥有自己的一块内存
函数原型:返回值类型 operator操作符(参数列表)
另:1.不能通过连接其他符号来创建新的操作符:比如operator@2.重载操作符必须有一个类类型参数(参数针对自定义类型不能是内置类型:int、char...)3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this指针5.【 .* 、 ::、 sizeof、 ?:、 . 】 注意以上5个运算符不能重载。这个要记住!!!
当运算符重载成全局时就需要调用的参数为类中公有的;但假如我们在实现时需要调用类中私有的参数呢?
我们就可以把operator放到类中这样就不仅可以调用公有的参数也可以调用私有的参数!
日期对比大小的函数:代码我贴这了!(0为假1为真)
- #include
- using namespace std;
-
- class Date
- {
- public:
- Date(int year=1, int month=1, int day=1)//构造函数-自动调用
- {
- _year = year;
- _month = month;
- _day = day;
- }
- ~Date()
- {
- _year = 0;
- _month = 0;
- _day = 0;
- }
-
- bool operator==(const Date& d)
- {
- return _year == d._year
- && _month == d._month
- && _day == d._day;
- }
- bool operator>(const Date& d)
- {
- if (_year > d._year)
- {
- return true;
- }
- else if (_year == d._year && _month > d._month)
- {
- return true;
- }
- else if (_year == d._year && _month == d._month && _day > d._day)
- {
- return true;
- }
- return false;
- }
- bool operator>=(const Date& d)
- {
- return *this > d|| *this==d;
- }
- bool operator<(const Date& d)
- {
- return 1 ^ (*this > d);
- }
- void Print()
- {
- cout << _year << "-" << _month << "-" << _day << endl;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
- int main()
- {
- Date d1(1988, 3, 23);
- Date d2(2020,9,24);
-