1.类与对象 (重点)
1.1 概念
类:类是一个抽象的概念,用于描述同一类对象的特点。
对象:根据类的概念所创造的实体。
【思考】:一个对象可以没有对应的类吗?
不可以,因为先有类才能创建对象。
【思考】:一个类可以没有对象吗?
可以的,但是对于现在的学习,如果只写了一个类,而没有对应的对象,这样的类没有意义。
1.2 类的内容
类中最基础的部分有两个:一个是属性,一个是行为。
- 属性:表示一些特征项的数值,比如说:身高、体重、年龄、性别。颜色、品牌、款式等等。而这些特征项的数值,使用变量 进行存储,而类中的变量也被称为"成员变量 "。
- 行为:能做什么事情?比如说:吃饭、睡觉、玩、唱、跳、rap、打篮球。行为一般通过函数 实现,而类中的函数也被称为"成员函数 "。
成员 = 成员函数+成员变量
【例子】以手机为例,来说明类的定义。
规定手机能够播放音乐、运行游戏、打电话、爆炸、游泳、飞、傻妞。手机有品牌、型号、重量等等属性。
#include <iostream>`
`using namespace std;`
`// 大驼峰命名法(帕斯卡命名法)`
`// 每个单词的首字母大写`
`class MobilePhone`
`{`
`public: // 权限:public最开放的权限`
` string brand; // 品牌`
` string model; // 型号`
` int weight; // 重量`
` void play_music()`
` {`
` cout << "只因你太美,哒哒哒" << endl;`
` }`
` void run_game()`
` {`
` cout << "王者、CF、三角洲、铲铲、打瓦、绝地求生、刺激战场、LOL、蛋仔" << endl;`
` }`
` void call()`
` {`
` cout << "光头强~ 在家吗~" << endl;`
` }`
`};`
`int main()`
`{`
` return 0;`
`}`
`
1.3 对象的创建
C++中存在两种类型的对象:
- 栈内存对象
对象所在的{}执行完毕后,自动被销毁。
#include <iostream>`
`using namespace std;`
`// 大驼峰命名法(帕斯卡命名法)`
`// 每个单词的首字母大写`
`class MobilePhone`
`{`
`public: // 权限:public最开放的权限`
` string brand; // 品牌`
` string model; // 型号`
` int weight; // 重量`
` void play_music()`
` {`
` cout << "只因你太美,哒哒哒" << endl;`
` }`
` void run_game()`
` {`
` cout << "王者、CF、三角洲、铲铲、打瓦、绝地求生、刺激战场、LOL、蛋仔" << endl;`
` }`
` void call()`
` {`
` cout << "光头强~ 在家吗~" << endl;`
` }`
`};`
`int main()`
`{`
` MobilePhone mp; // 栈内存对象`
` mp.brand = "苹果";`
` mp.model = "17 pro max max";`
` mp.weight = 2000;`
` cout << mp.brand << " " << mp.model << " " << mp.weight << endl;`
` mp.play_music();`
` mp.run_game();`
` mp.call();`
` return 0;`
`}`
`
- 堆内存对象
必须使用new关键字创建,使用指针保存,如果不使用delete关键字销毁,则堆内存对象持续存在,导致内存泄漏。->
#include <iostream>`
`using namespace std;`
`// 大驼峰命名法(帕斯卡命名法)`
`// 每个单词的首字母大写`
`class MobilePhone`
`{`
`public: // 权限:public最开放的权限`
` string brand; // 品牌`
` string model; // 型号`
` int weight; // 重量`
` void play_music()`
` {`
` cout << "只因你太美,哒哒哒" << endl;`
` }`
` void run_game()`
` {`
` cout << "王者、CF、三角洲、铲铲、打瓦、绝地求生、刺激战场、LOL、蛋仔" << endl;`
` }`
` void call()`
` {`
` cout << "光头强~ 在家吗~" << endl;`
` }`
`};`
`int main()`
`{`
` MobilePhone *mp = new MobilePhone; // 堆内存对象`
` mp->brand = "华为";`
` mp->model = "三折叠怎么叠都有面";`
` mp->weight = 400;`
` cout << mp->brand << " " << mp->model << " "<< mp->weight << endl;`
` mp->play_music();`
` mp->run_game();`
` mp->call();`
` delete mp; // 手动销毁`
` mp = NULL; // 建议指向空`
` return 0;`
`}`
`
思考:
malloc和free与new和delete他们的区别是什么?
2.封装 (重点)
在上一小节中的MobilePhone类与结构体差别不大,实际上可以认为C++的结构体就是一种完全开放的类。
封装指的是,将类的一些属性和行为进行隐藏。重新提供外部的访问接口,封装可以提升程序的安全性,并且可以让程序员更关注上层架构而非内部细节。
#include <iostream>`
`using namespace std;`
`// 大驼峰命名法(帕斯卡命名法)`
`// 每个单词的首字母大写`
`class MobilePhone`
`{`
`private: // 私有权限,最封闭的权限,只能在内中进行访问`
` string brand; // 品牌`
` string model; // 型号`
` int weight = 200; // 重量`
`public: // 权限:public最开放的权限`
` // 对于品牌可以修改和访问`
` // 对于型号可以修改不能访问`
` // 对于重量可以访问,但是不能修改`
` // get 读函数`
` string get_brand()`
` {`
` return brand;`
` }`
` // 写函数`
` void set_brand(string b)`
` {`
` brand = b;`
` }`
` // model 写函数`
` void set_model(string m)`
` {`
` model = m;`
` }`
` // get weight读函数`
` int get_weight()`
` {`
` return weight;`
` }`
`};`
`int main()`
`{`
` MobilePhone *mp = new MobilePhone; // 堆内存对象`
` mp->set_brand("oppo");`
` cout << mp->get_brand() << endl;`
` mp->set_model("a5");`
` cout << mp->get_weight() << endl;`
` delete mp; // 手动销毁`
` mp = NULL; // 建议指向空`
` return 0;`
`}`
`
3.构造函数 (重点)
3.1 基本使用
构造函数一种特殊的成员函数,用于创建对象时初始化,创建对象时必须直接或者间接调用当前类的任意一个构造函数。
- 函数名称跟类名完全相同
- 构造函数不写返回值
- 如果程序员不手动编写构造函数,编译器就会自动添加一个默认无参构造函数

手动添加任意构造函数后,编译器就不会添加默认无参构造函数了。
#include <iostream>`
`using namespace std;`
`class MobilePhone`
`{`
`private: // 私有权限,最封闭的权限,只能在内中进行访问`
` string brand; // 品牌`
` string model; // 型号`
` int weight; // 重量`
`public:`
` // 编译器默认添加的默认无参构造函数`
` MobilePhone()`
` {`
` brand = "小米";`
` model = "18";`
` weight = 300;`
` }`
` string get_brand()`
` {`
` return brand;`
` }`
` string get_model()`
` {`
` return model;`
` }`
` int get_weight()`
` {`
` return weight;`
` }`
`};`
`int main()`
`{`
` MobilePhone *mp = new MobilePhone; // 堆内存对象`
` cout << mp->get_brand() << " " << mp->get_model() << " " << mp->get_weight() << endl;`
` return 0;`
`}`
`
有参构造函数:
#include <iostream>`
`using namespace std;`
`class MobilePhone`
`{`
`private: // 私有权限,最封闭的权限,只能在内中进行访问`
` string brand; // 品牌`
` string model; // 型号`
` int weight; // 重量`
`public:`
` // 编译器默认添加的默认无参构造函数`
` MobilePhone(string b,string m,int w)`
` {`
` brand = b;`
` model = m;`
` weight = w;`
` }`
` string get_brand()`
` {`
` return brand;`
` }`
` string get_model()`
` {`
` return model;`
` }`
` int get_weight()`
` {`
` return weight;`
` }`
`};`
`int main()`
`{`
` MobilePhone *mp = new MobilePhone("华为","17",300); // 堆内存对象`
` cout << mp->get_brand() << " " << mp->get_model() << " " << mp->get_weight() << endl;`
` return 0;`
`}`
`
构造函数重载:
#include <iostream>`
`using namespace std;`
`class MobilePhone`
`{`
`private: // 私有权限,最封闭的权限,只能在内中进行访问`
` string brand; // 品牌`
` string model; // 型号`
` int weight; // 重量`
`public:`
` // 编译器默认添加的默认无参构造函数`
` MobilePhone(string b,string m,int w)`
` {`
` brand = b;`
` model = m;`
` weight = w;`
` }`
` MobilePhone()`
` {`
` brand = "8848";`
` model = "钛金手机巅峰版鳄鱼皮";`
` weight = 500;`
` }`
` string get_brand()`
` {`
` return brand;`
` }`
` string get_model()`
` {`
` return model;`
` }`
` int get_weight()`
` {`
` return weight;`
` }`
`};`
`int main()`
`{`
` MobilePhone *mp = new MobilePhone("华为","17",300); // 堆内存对象`
` cout << mp->get_brand() << " " << mp->get_model() << " " << mp->get_weight() << endl;`
` // 错误,注意栈内存对象,调用无参构造函数是,不需要添加()`
`// MobilePhone mp1(); // 错误 这是函数声明,不是创建对象`
`// mp1.get_brand();`
` MobilePhone mp1("vivo","23",33);`
` mp1.get_brand();`
` return 0;`
`}`
`
- 构造函数还支持函数参数默认值
#include <iostream>`
`using namespace std;`
`class MobilePhone`
`{`
`private: // 私有权限,最封闭的权限,只能在内中进行访问`
` string brand; // 品牌`
` string model; // 型号`
` int weight; // 重量`
`public:`
` MobilePhone(string b = "虾米",string m = "耙子",int w = 300)`
` {`
` brand = b;`
` model = m;`
` weight = w;`
` }`
`// MobilePhone()`
`// {`
`// brand = "8848";`
`// model = "钛金手机巅峰版鳄鱼皮";`
`// weight = 500;`
`// }`
` string get_brand()`
` {`
` return brand;`
` }`
` string get_model()`
` {`
` return model;`
` }`
` int get_weight()`
` {`
` return weight;`
` }`
`};`
`int main()`
`{`
` MobilePhone mp;`
` cout << mp.get_brand() << mp.get_model() << mp.get_weight() << endl;`
` return 0;`
`}`
`
3.2 构造初始化列表
构造初始化列表是一种更简单的给成员变量赋值初始化的写法。
#include <iostream>`
`using namespace std;`
`class MobilePhone`
`{`
`private: // 私有权限,最封闭的权限,只能在内中进行访问`
` string brand; // 品牌`
` string model; // 型号`
` int weight; // 重量`
`public:`
` MobilePhone(string b,string m,int w)`
` :brand(b),model(m),weight(w){}`
` MobilePhone()`
` :brand("8848"),model("钛金手机巅峰版鳄鱼皮"),weight(500){}`
` string get_brand()`
` {`
` return brand;`
` }`
` string get_model()`
` {`
` return model;`
` }`
` int get_weight()`
` {`
` return weight;`
` }`
`};`
`int main()`
`{`
` MobilePhone mp;`
` cout << mp.get_brand() << mp.get_model() << mp.get_weight() << endl;`
` return 0;`
`}`
`
当构造函数的局部变量与成员变量重名时,除了后面学习的this指针外,也可以使用构造初始化列表区分。

3.3 隐式调用 & 显式调用构造函数
构造函数的调用可以分为显式调用 和隐式调用 。
显式调用:在创建对象时手写构造函数的参数列表(显式指定参数列表)。
隐式调用:在创建对象时不写构造函数的参数列表。编译器会尝试调用对应参数的构造函数。
#include <iostream>`
`using namespace std;`
`class Student`
`{`
`private:`
` int age;`
`public:`
` explicit Student(int a):age(a)`
` {`
` cout << "构建函数被调用了" << endl;`
` }`
` Student()`
` {`
` }`
` int get_age()`
` {`
` return age;`
` }`
`};`
`int main()`
`{`
` Student s1(12); // 显式调用`
` cout << s1.get_age() << endl;`
` Student s3 = Student(14); // 显式调用`
` cout << s3.get_age() << endl;`
` Student s4 = 15; // 隐式调用`
` cout << s4.get_age() << endl;`
` Student *s2 = new Student(12); // 显式调用`
` cout << s2->get_age() << endl;`
` Student s5; // 隐式调用构造函数`
` return 0;`
`}`
`
建议大家使用显式调用,可以使用explicit关键字屏蔽隐式调用语法。我们工作学习中尤其老代码有很多就是隐式调用多了导致变成shi山代码的。
3.4 拷贝构造函数
3.4.1 概念
当程序员不手写拷贝构造函数时,编译器会自动添加一个拷贝构造函数,使对象创建可以通过这个构造函数实现。
#include <iostream>`
`using namespace std;`
`class Student`
`{`
`private:`
` int age;`
`public:`
` Student(int a):age(a)`
` {`
` cout << "构建函数被调用了" << endl;`
` }`
` // 手动编写,编译器自动添加的拷贝构造函数`
` Student(const Student &st)`
` {`
` cout << "拷贝构造函数被调用了" << endl;`
` age = st.age;`
` }`
` int get_age()`
` {`
` return age;`
` }`
`};`
`int main()`
`{`
` Student s1(2); // 调用有参构造函数`
` Student s3(s1); // 调用拷贝构造函数,显式调用拷贝构造函数`
` cout << s3.get_age() << endl; // 2`
` return 0;`
`}`
`
【思考】拷贝构造函数是否存在安全隐患?
存在,当成员变量出现指针类型时,默认的拷贝构造函数会导致两个对象的成员变量指针指向同一处,不符合面向对象的设计规范,并且也是不安全的,这种现象也被称为"浅拷贝"。
3.4.2 浅拷贝

#include <iostream>`
`#include <string.h>`
`using namespace std;`
`class Dog`
`{`
`private:`
` char *name;`
`public:`
` Dog(char *n)`
` {`
` name = n;`
` }`
` void show_name()`
` {`
` cout << name << endl;`
` }`
`};`
`int main()`
`{`
` char arr[20] = "旺财";`
` Dog d1(arr);`
` Dog d2(d1); // 拷贝构造函数`
` strcpy(arr,"大黄");`
` d1.show_name(); // 大黄`
` d2.show_name(); // 大黄`
` return 0;`
`}`
`
3.4.3 深拷贝
这种情况必须手动重新编写拷贝构造函数,使每次赋值都创建一个新的副本,从而每个对象单独持有自己的成员变量。这种方式也被称为"深拷贝"。

#include <iostream>`
`#include <string.h>`
`using namespace std;`
`class Dog`
`{`
`private:`
` char *name;`
`public:`
` Dog(char *n)`
` {`
` name = new char[20];`
` strcpy(name,n);`
` }`
` Dog(Dog &d)`
` {`
` name = new char[20];`
` strcpy(name,d.name);`
` }`
` void show_name()`
` {`
` cout << name << endl;`
` }`
`};`
`int main()`
`{`
` char arr[20] = "旺财";`
` Dog d1(arr);`
` Dog d2(d1); // 拷贝构造函数`
` strcpy(arr,"大黄");`
` d1.show_name(); // 旺财`
` d2.show_name(); // 旺财`
` return 0;`
`}`
`
【思考】上述代码有没有需要优化的地方?
存在,new开辟的空间没有释放,造成了内存泄漏。
总结:
如果在类中没有任何构造函数,编译器会自动添加,无参构造函数,拷贝构造函数
如果在类中添加了一个构造函数,编译器将不再添加默认无参构造函数
如果在类中添加了一个拷贝构造函数,编译器将不再添加拷贝构造函数,和默认无参构造函数(取决编译器)。
4、析构函数 (掌握)
析构函数是与构造函数完全对立的函数。
|---------------------|---------------|
| 构造函数 | 析构函数 |
| 创建对象时手动调用 | 当对象销毁时,自动调用 |
| 函数名称是类名 | 函数名称是~类名 |
| 构造函数可以重载 | 析构函数没有参数,不能重载 |
| 用于创建对象时并初始化 | 用于销毁对象时释放资源 |
| 有返回值但是不写,返回值是新创建的对象 | 没有返回值 |
#include <iostream>`
`#include <string.h>`
`using namespace std;`
`class Dog`
`{`
`private:`
` char *name;`
`public:`
` Dog(char *n)`
` {`
` name = new char[20];`
` strcpy(name,n);`
` }`
` Dog(Dog &d)`
` {`
` name = new char[20];`
` strcpy(name,d.name);`
` }`
` void show_name()`
` {`
` cout << name << endl;`
` }`
` ~Dog()`
` {`
` cout << "析构函数被调用了" << endl;`
` // [] 表示释放的空间是一个数组`
` delete []name;`
` }`
`};`
`int main()`
`{`
` cout << "程序开始运行" << endl;`
` {`
` char arr[20] = "旺财";`
` Dog d1(arr);`
` Dog d2(d1); // 拷贝构造函数`
` strcpy(arr,"大黄");`
` d1.show_name(); // 旺财`
` d2.show_name(); // 旺财`
` }`
` cout << "程序运行结束" << endl;`
` return 0;`
`}`
`
如果没有手动编写析构函数,编译器会自动添加一个析构函数,但是析构函数是空的。
5.作用域限定符:: (掌握)
5.1 名字空间
#include <iostream>`
`using namespace std;`
`// 名字空间`
`namespace my_space`
`{`
` int a = 3;`
` int b = 4;`
`}`
`using namespace my_space;`
`int a = 2;`
`int main()`
`{`
` int a = 1;`
` cout << a << endl; // 1 就近原则,打印1`
` cout << ::a << endl; // :: 匿名名字空间(全局空间) 2`
` cout << my_space::a << endl; // 3`
` cout << b << endl; // 4`
` return 0;`
`}`
`
5.2 类内声明,类外定义
#include <iostream>`
`using namespace std;`
`class Demo`
`{`
`public:`
` // 类内声明`
` Demo();`
` void test(string str);`
`};`
`// 类外定义`
`Demo::Demo()`
`{`
` cout << "构造函数被调用啦" << endl;`
`}`
`void Demo::test(string str)`
`{`
` cout << str << endl;`
`}`
`int main()`
`{`
` Demo d;`
` d.test("2233");`
` return 0;`
`}`
`
6.this指针 (掌握)
6.1 概念
this指针是一个特殊的指针,指向当前类对象的首地址。
成员函数(包括构造函数和析构函数)中都有this指针,因此this指针只能在类内部使用。实际上this指向的就是当前运行的成员函数所绑定的对象。
#include <iostream>`
`using namespace std;`
`class Test`
`{`
`public:`
` Test()`
` {`
` }`
` void test_this()`
` {`
` cout << this << endl;`
` }`
`};`
`int main()`
`{`
` Test t1;`
` cout << &t1 << endl; // 0x61fe8f`
` t1.test_this(); // 0x61fe8f`
` Test t2();`
` cout << &t2 << endl; // 0x61fe8e`
` t2.test_this(); // 0x61fe8e`
` Test *t3 = new Test;`
` cout << t3 << endl;`
` t3->test_this();`
` return 0;`
`}`
`
6.2 功能
6.2.1 类内调用成员
- 成员(成员变量+成员函数)必须由对象来调用。类中成员的调用都依赖于this指针,通常由编译器帮你补充。
#include <iostream>`
`using namespace std;`
`class Test`
`{`
`private:`
` string name;`
`public:`
` Test(string n)`
` {`
` // 编译器会默认添加this指针指向当前对象,调用成员`
` this->name = n;`
` }`
` string get_name()`
` {`
` // 编译器会默认添加this指针指向当前对象,调用成员`
` return this->name;`
` }`
`};`
`int main()`
`{`
` Test t1("张三");`
` cout << &t1 << endl; // 0x61fe8f`
` return 0;`
`}`
`
6.2 .1 区分重名的成员变量和局部变量
#include <iostream>`
`using namespace std;`
`class Test`
`{`
`private:`
` string name;`
`public:`
` Test(string name)`
` {`
` this->name = name;`
` }`
` string get_name()`
` {`
` return this->name;`
` }`
`};`
`int main()`
`{`
` Test t1("张三");`
` cout << &t1 << endl; // 0x61fe8f`
` return 0;`
`}`
`
6.2.3 链式调用
一个函数如果支持链式调用,它的返回值类型是一个对象。
#include <iostream>`
`using namespace std;`
`class Test`
`{`
`private:`
` int val = 0;`
`public:`
` Test& add(int i)`
` {`
` val += i;`
` return *this;`
` }`
` int get_val()`
` {`
` return val;`
` }`
`};`
`int main()`
`{`
` Test t1;`
` t1.add(1);`
` t1.add(2);`
` t1.add(100);`
` cout << t1.get_val() << endl; // 103`
` Test t2;`
` cout << t2.add(2).add(20).add(100).get_val() << endl; // 122`
` cout << t2.get_val() << endl; // 122`
` return 0;`
`}`
`
7.static关键字 (掌握)
7.1 静态局部变量
使用static修饰局部变量,这样的变量就是静态局部变量。
静态局部变量在第一次调用时创建,直到程序结束后销毁,同一个类的所有对象共用这一份静态局部变量。
#include <iostream>`
`using namespace std;`
`class Test`
`{`
`public:`
` void func()`
` {`
` int a = 1;`
` // 静态局部变量`
` static int b = 1;`
` cout << "a=" << ++a << endl;`
` cout << "b=" << ++b << endl;`
` }`
`};`
`int main()`
`{`
` Test t1;`
` t1.func(); // 2 2`
` t1.func(); // 2 3`
` Test t2;`
` t2.func(); // 2 4`
` Test t3;`
` t3.func(); // 2 5`
` return 0;`
`}`
`
7.2 静态成员变量
使用static修饰成员变量,这样的变量就是静态成员变量。
- 静态成员变量需要在类内声明,类外初始化。
- 一个类的所有对象共用一份静态成员变量。
- 静态成员变量可以脱离对象使用,可以直接使用类名::方式进行调用。(推荐使用类名::的方式调用)
- 静态成员变量在程序开始运行时就开辟内存空间,直到程序运行结束时销毁。
#include <iostream>`
`using namespace std;`
`class Test`
`{`
`public:`
` int a = 1;`
`// static int b = 2; // 错误 静态成员变量需要在类内声明,类外初始化`
` static int b;`
`};`
`// 类外初始化`
`int Test::b = 1;`
`int main()`
`{`
` cout << Test::b << " " << &Test::b << endl;`
` Test t1;`
` cout << ++t1.a << " " << ++t1.b << endl; // 2 2`
` cout << ++t1.a << " " << ++t1.b << endl; // 3 3`
` Test t2;`
` // 多个对象共用一份静态成员变量`
` cout << ++t2.a << " " << ++t2.b << endl; // 2 4`
`// cout << Test::a << endl; // 错误 a是类中的普通成员变量,依赖于对象调用`
` // 静态成员变量可以脱离对象使用`
` cout << Test::b << " " << &Test::b << endl;`
` return 0;`
`}`
`
7.3 静态成员函数
使用static修饰成员函数,这样的函数就是静态成员函数。
- 静态成员函数,可以脱离对象使用,可以通过类名::方式直接调用。
- 静态成员函数,可以直接调用当前类中非静态成员嘛?(不能,因为没有this指针)
- 静态成员函数,可以调用静态成员函数嘛?(可以)
- 静态成员函数,可以调用静态成员变量嘛?(可以)
#include <iostream>`
`using namespace std;`
`class Test`
`{`
`public:`
` int a = 20;`
` static int b;`
` void func0()`
` {`
` // 普通成员函数可以直接调用静态成员函数`
`// func1();`
` cout << "非静态成员函数" << endl;`
` }`
` static void func1()`
` {`
` func2();`
`// func0(); // 错误 静态成员函数,不能直接调用非静态成员函数`
` cout << "静态成员函数1" << endl;`
` }`
` static void func2()`
` {`
` cout << b << endl;`
` cout << "静态成员函数2" << endl;`
` }`
`};`
`int Test::b = 2;`
`int main()`
`{`
` // 静态成员函数可以通过类名::的方式直接访问`
`// Test::func1();`
`// Test::func0(); // 错误 普通成员函数 无法 通过类名::方式直接访问`
` Test t1;`
`// t1.func0();`
` t1.func1();`
`// t1.func2();`
` return 0;`
`}`
`
【思考】:
如果想在静态成员函数中调用当前类的非静态成员,需要怎么做?
如果需要在静态成员函数中调用非静态成员,可以通过参数将对象传递进来,因为静态成员函数没有this指针,无法直接调用。
#include <iostream>`
`using namespace std;`
`class Test`
`{`
`public:`
` int a = 20;`
` static int b;`
` void func0()`
` {`
` // 普通成员函数可以直接调用静态成员函数`
`// func1();`
` cout << "非静态成员函数" << endl;`
` }`
` static void func1(Test &t)`
` {`
` cout << t.a << endl;`
` t.func0();`
` Test t2;`
` t2.func0();`
` cout << "静态成员函数1" << endl;`
` }`
` static void func2()`
` {`
` cout << b << endl;`
` cout << "静态成员函数2" << endl;`
` }`
`};`
`int Test::b = 2;`
`int main()`
`{`
` // 静态成员函数可以通过类名::的方式直接访问`
`// Test::func1();`
`// Test::func0(); // 错误 普通成员函数 无法 通过类名::方式直接访问`
` Test t1;`
`// t1.func0();`
` t1.func1(t1);`
`// t1.func2();`
` return 0;`
`}`
`
7.4 单例设计模式 (了解)
设计模式是一套被反复使用、多人知晓、经过分类的,代码设计经验的总结。通常用于一些面向对象的语言,如:java、C++、C#等。
本章以一个简化版本的单例设计模式为例,讲解static的实际作用。
表示唯一的对象。
#include <iostream>`
`using namespace std;`
`// 懒汉式\饿汉式`
`// 懒汉式是有安全隐患的,多线程情况下可能会出现此安全隐患,可以通过加锁解决.`
`// 饿汉式没有安全隐患`
`// 单例设计模式`
`class Singleton`
`{`
`private:`
` Singleton(){}`
` Singleton(const Singleton&){}`
` static Singleton *instance; // 静态成员变量指针`
`// ~Singleton(){}`
`public:`
` static Singleton* get_instance()`
` {`
` if(instance == NULL)`
` {`
` instance = new Singleton;`
` }`
` return instance;`
` }`
` static void delete_instance()`
` {`
` if(instance != NULL)`
` {`
` delete instance;`
` instance = NULL;`
` }`
` }`
`};`
`// 类外初始化`
`Singleton *Singleton::instance = NULL;`
`int main()`
`{`
` Singleton *s1 = Singleton::get_instance();`
` Singleton *s2 = Singleton::get_instance();`
` cout << s1 << endl;`
` cout << s2 << endl;`
` return 0;`
`}`
`
8.const 关键字 (掌握)
8.1 const修饰成员函数
const修饰的成员函数,表示常成员函数。特性如下:
- 常成员函数可以调用当前类的普通成员变量,但是不能修改
- 常成员函数不能调用当前类中的普通成员函数,哪怕这个函数中没有做任何成员变量的修改
- 常成员函数,可以访问当前类中的常成员函数
- 常成员函数对于当前函数中的局部变量可以修改和定义
建议只要成员函数不修改成员变量,就是用const修饰,例如show、get等。
#include <iostream>`
`using namespace std;`
`class Demo`
`{`
`private:`
` int a;`
`public:`
` Demo(int a)`
` {`
` this->a = a;`
` }`
` void func0()`
` {`
` cout << "哈哈哈哈哈" << endl;`
` }`
` // 常成员函数`
` int get_demo() const`
` {`
` return a;`
` }`
` // 常成员函数`
` void test() const`
` {`
` // 常成员函数可以调用当前类的普通成员变量`
` cout << a << endl;`
` // 常成员函数可以调用当前类的普通成员变量,但是不能修改`
`// a = 10;`
` // 常成员函数不能调用当前类中的普通成员函数,哪怕这个函数中没有做任何成员变量的修改`
`// func0();`
` // 常成员函数,可以访问当前类中的常成员函数`
` get_demo();`
` // 当前函数中的局部变量可以修改和定义`
` int x = 0;`
` x = 20;`
` }`
`};`
`int main()`
`{`
` Demo d(1);`
` d.test();`
` return 0;`
`}
8.2 const修饰对象
const修饰的对象被称为常量对象。
- 常量对象可以调用当前类的常量函数
- 常量对象无法直接调用当前类的普通成员函数
- 常量对象可以调用当前类的普通成员变量
- 常量对象可以调用当前类的普通成员变量,但是不能修改
#include <iostream>`
`using namespace std;`
`class Demo`
`{`
`private:`
` int a;`
`public:`
` int b = 20;`
` Demo(int a)`
` {`
` this->a = a;`
` }`
` void func0()`
` {`
` cout << "哈哈哈哈哈" << endl;`
` }`
` // 常成员函数`
` int get_demo() const`
` {`
` return a;`
` }`
` // 常成员函数`
` void test(const Demo &d) const`
` {`
` // 常成员函数可以调用当前类的普通成员变量`
` cout << a << endl;`
` // 常成员函数可以调用当前类的普通成员变量,但是不能修改`
`// a = 10;`
` // 常成员函数不能调用当前类中的普通成员函数,哪怕这个函数中没有做任何成员变量的修改`
`// func0();`
` // 常成员函数,可以访问当前类中的常成员函数`
` get_demo();`
` // 当前函数中的局部变量可以修改和定义`
` int x = 0;`
` x = 20;`
` }`
`};`
`int main()`
`{`
` // 常量对象`
`// const Demo demo(1);`
` Demo const demo(1); // 等效于上一行`
` // 常量对象可以调用当前类的常量函数`
` cout << demo.get_demo() << endl;`
` // 常量对象无法直接调用当前类的普通成员函数`
`// demo.func0();`
` // 常量对象可以调用当前类的普通成员变量`
` cout << demo.b << endl;`
` // 常量对象可以调用当前类的普通成员变量,但是不能修改`
`// demo.b = 10;`
` return 0;`
`}`
`
8.3 const修饰成员变量
const修饰成员变量被称为常成员变量,表示该成员变量的值无法被修改。
常成员变量有两种初始化的方式:
- 直接初始化
- 构造初始化列表
如果两种赋值方式同时出现以构造初始化列表的方式为准
#include <iostream>`
`using namespace std;`
`class Demo`
`{`
`private:`
` // 常成员变量`
` const int a = 1; // 直接初始化赋值`
` const int b = 2;`
` const int c = 3;`
`public:`
` Demo(int a,int b,int c)`
` :a(a),b(b),c(c)`
` {`
` // 二次赋值`
`// this->a = a;`
`// this->b = b;`
` }`
` void show()`
` {`
` cout << a << " " << b << " " << c << endl;`
` }`
` void test()`
` {`
` // 常成员变量不能直接修改`
`// a++;`
`// b++;`
`// c++;`
` }`
`};`
`int main()`
`{`
` Demo d(10,20,30);`
` d.show();`
` return 0;`
`}`
`
8.4 const修饰局部变量
const修饰局部变量,表示该局部变量不可被修改。
需要直接赋值初始化。否则后续无法初始化。
一般这种写法用在引用参数。
#include <iostream>`
`using namespace std;`
`class Demo`
`{`
`private:`
` // 常成员变量`
` const int a = 1; // 直接初始化赋值`
` const int b = 2;`
` const int c = 3;`
`public:`
` Demo(int a,int b,int c)`
` :a(a),b(b),c(c)`
` {`
` // 二次赋值`
`// this->a = a;`
`// this->b = b;`
` }`
` void show()`
` {`
` cout << a << " " << b << " " << c << endl;`
` }`
` void test(const int &a)`
` {`
` int a1 = 10;`
` a1 = 20;`
` const int e = 1;`
`// e = 20;`
` }`
`};`
`int main()`
`{`
` Demo d(10,20,30);`
` d.show();`
` return 0;`
`}