#include#include using std::cout; using std::endl; // 类的声明放在ClassName.h 中, // 类的定义放在ClassName.cpp 中! // 开源项目一般将类的声明和定义放在一起ClassName.hpp 中! class Person { public: void showInfo(); // .h 中声明! private: char* _name; char* _sex; int _age; }; // .cpp 中定义! void Person::showInfo() { cout << _name << "-" << _sex << "-" << _age << endl; } // 类的访问限定: // public: 修饰的成员在类外可以直接访问! // private和protected: 修饰的成员在类外不能直接访问! // class 的默认访问权限是private,struct是public; // 类的实例化! // 类只是一个模型一样的东西,限定了类有那些成员,定义出一个类并没有分配实际的内存空间来存储它! // 一个类可以实例化出多个对象,实例化的对象占实际的物理空间,存储成员变量! // 对象是如何存储的? // 类的变量的值可以是不一样的,但是成员函数的代码是相同的! class A1 { public: void f1() {}; private: int _a; }; class A2 { public: void f2() {}; }; class A3 { }; #if 0 // 可以发现,对象存储时,只将类的成员变量每个成员单独一份,代码是共享的! int main() { A1 a1; A2 a2; A3 a3; cout << sizeof(a1) <<" "<< sizeof(a2) <<" "<< sizeof(a3) << endl; return 0; } #endif // 结构体内存对齐规则: // 第一个成员在与结构体偏移量为 0 的地址处! // 其他成员变量要对齐到某个数字(对齐数 = 编译器默认的堆区数与该成员的大小的较小值!)的整数倍的地址处! // 结构体的总大小为: 最大对齐数的整数倍(min(所有变量类型最大者,编译器指定对齐数)) // 嵌套大小,嵌套的结构体算作一个成员! // this 指针: class Date { public: void Display() { cout << _year << "-" << _month << "-" << _day << endl; } void SetDate(int year, int month, int day) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; // C++ 编译器给每个非静态成员函数增加了一个隐藏的this指针,让该指针指向当前对象,在函数体中所有成员变量的操作都是通过该指针去访问的 // 只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成! #if 0 int main() { Date d1, d2; d1.SetDate(2022, 7, 6); d2.SetDate(2022, 7, 7); d1.Display(); d2.Display(); return 0; } #endif // this 指针的特性: // 1. this 指针的类型: 类类型* const // 2. 只能在成员函数的内部使用! // 3. this 指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参. 所以对象中不存储this指针! // 4. this 指针是成员函数第一个隐含的指针参数,一般情况下由编译器通过ecx寄存器自动传递,不需要用户传递! class A { public: void PrintA() { cout << _a << endl; } void Show() { cout << "Show()" << endl; } private: int _a; }; #if 0 //. this 指针的类型: 类类型* const int main() { Date* p = NULL; //p->PrintA(); //p->Show(); return 0; } #endif //默认成员函数: // 初始化和清理: 构造函数,析构函数! // 拷贝复制: 使用同类对象初始化创建新对象! 赋值重载主要是把一个对象赋值给另一个对象! // 取地址重载: 主要是普通对象和 const 对象取地址! // 1. 构造函数: // 构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值 // 并且在对象的生命周期只调用一次! // 如果一个类没有写构造函数,编译器会自动生成一个无参构造函数! // 一旦用户显示定义了构造函数,则不会再生成默认的无参构造! class Time { public: Time() { cout << "Time()" << endl; _hour = 0; _minute = 0; _second = 0; } private: int _hour; int _minute; int _second; }; class Datr{ private: // 基本类型,默认赋值! int _year; int _month; int _day; // 自定义类型,调用其无参构造赋值! Time _t; }; #if 0 int mian() { Datr d; return 0; } #endif // 2. 析构函数 // 与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是编译器完成的! // 而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作! // 特征: // (1) 析构函数名是在类名前加上~ // (2) 无参数无返回值! // (3) 一个类有且之后一个析构函数。若未显示定义,系统会默认生成析构函数! // (4) 对象生命周期结束时,C++编译器系统会自动调用析构函数! typedef int DataType; class SeqList { public: SeqList(int capacity = 10) :_capacity(capacity) { _pData = (DataType*)malloc(capacity * sizeof(DataType)); assert(_pData); _size = 0; } ~SeqList() { cout << "~SeqList()" << endl; free(_pData); // 释放堆上的空间! _pData = nullptr; _capacity = 0; _size = 0; } private: int * _pData; size_t _size; size_t _capacity; }; #if 0 int main() { SeqList sq; return 0; } #endif // 3. 拷贝构造函数 // 构造函数只有一个形参,且该形参时本类类型对象的引用(一般是 const的),在用已存在的类类型对象创建新对象时由编译器自动调用! // 特征: // 拷贝构造函数时构造函数的一个重载形式! // 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用! // 如果没有显示定义拷贝函数系统会自动生成拷贝构造函数,默认的拷贝构造函数对象按内存存储字节完成拷贝!(浅拷贝!) class Data { public: Data(int year = 2022,int month = 1, int day = 1) :_year(year), _month(month), _day(day) {} Data(const Data& data) { _year = data._year; _month = data._month; _day = data._day; } private: int _year; int _month; int _day; }; // 运算符重载: // C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型, // 函数名字以及参数列表,其返回值类型与参数列表与普通函数类似! // 函数名字为: 关键字operator后面接需要重载的运算符符号 // 函数原型: 返回值类型 operator操作符(参数列表) // 内置类型的操作符,其含义不能改变; // . * :: sizeof ?:不能重载! // 全局operator== class DateA { public: DateA(int year = 1900, int month = 1, int day = 1) :_year(year), _month(month), _day(day) {} bool operator==(const DateA& d) { return _day == d._day && _month == d._month && _year == d._year; } DateA& operator=(const DateA& d) { _year = d._year; _day = d._day; _month = d._month; } int _year; int _month; int _day; }; void Test() { DateA d1(1,2,3); DateA d2(2,4,5); cout << (d1 == d2) << endl; return; } #if 0 int main() { Test(); return 0; } #endif // 7. const 成员 // const 修饰尘缘函数: const 修饰的成员函数称为const成员函数,实际修饰该成员函数隐含的this指针,表明该成员函数中不能对类的任何成员进行修改! class AnoutConst { public: void show1() { cout << "show()" << endl; } void call(int val) { _val = val; show(); } void show()const { cout << "show() const" << endl; // call(3); const 成员函数不能调用非const成员函数! } private: int _val; }; // 取地址以及const 取地址操作符重载! class AboutCon { public: AboutCon* operator&() { return this; } const AboutCon* operator&() const { return this; } private: int _val; }; // 构造方法的初始化列表: // 1. 每个成员变量在初始化列表中只能出现一次! // 2. 类中包含一下成员必须放在初始化列表位置进行初始化! // (1) 引用成员变量 // (2) const 成员变量 // (3) 自定义类型成员变量(该类没有默认构造函数!) // 3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量一定会使用初始化列表初始化! // 4. 成员在类中声明次序就是其在初始化列表中的初始化顺序,与其在列表中的先后次序无关! // 对于单个参数的构造函数,构造函数有类型装欢的作用! // 使用 explict 修饰构造函数,将会禁止单构造函数隐式转换! // static 成员: // 声明为static的类成员,称为类的静态成员. // 静态成员变量一定要在类的外部进行初始化! class B { public: B() { _count++; } B(const B& b) { ++_count; } static int GetCount() { return _count; } private: static int _count; }; int B::_count = 0; // 静态成员为所有类对象所共享,不属于某个具体的实例! // 静态成员变量必须在类外定义,定义时不添加static关键字! // 静态成员没有隐藏的this指针,不能访问任何非静态成员! // 静态成员也有访问级别! // 友元: 突破封装的一种便利! // 友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但是需要在类的内部声明,声明时要加上friend关键字! // 友元函数不能用const修饰! // 友元类: 关系是单项的,A声明B是friend那么B就能访问A的全部,但是A不能访问B的全部! // 内部类: // 如过类B定义在类A的内部,那么类B就叫做内部类,但是B是独立的,不依赖A存在而存在! // 内部类就是外部类的友元类,但是外部类不是内部类的友元类! // 内部类可以直接访问外部类中的static、枚举成员、不需要外部类的对象/类名! // sizeof(外部类) = 外部类 ,和内部类没有任何关系!



