C++~01面向对象基础

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修饰成员变量被称为常成员变量,表示该成员变量的值无法被修改。

常成员变量有两种初始化的方式:

  1. 直接初始化
  2. 构造初始化列表

如果两种赋值方式同时出现以构造初始化列表的方式为准

复制代码
#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;`
`}
相关推荐
会编程的土豆1 小时前
Go 方法接收者超清晰笔记(类型名 vs 变量名)
开发语言·笔记·golang
郝学胜-神的一滴2 小时前
干货版《算法导论》07:递归视角下的选择排序与归并排序
java·数据结构·c++·python·程序人生·算法·排序算法
YY&DS2 小时前
Qt 嵌入 CEF 在 Linux 下必须设置 `QT_XCB_GL_INTEGRATION=xcb_egl才能加载网页
linux·开发语言·qt
csdn_aspnet2 小时前
javascript 算法 LeetCode 编号 70 - 爬楼梯
开发语言·javascript·算法·leetcode·ecmascript
han_hanker2 小时前
BeanUtils.copyProperties 和序列化的问题
java·开发语言·spring boot
野生技术架构师2 小时前
牛客网2026互联网大厂Java面试题汇总,附官方级答案解析
java·开发语言
暖焰核心3 小时前
C++内存管理和模板初阶
开发语言·c++
feeday3 小时前
gpt4o 图像反推提示词
开发语言·人工智能·python
Irissgwe3 小时前
c++智能指针
开发语言·c++