C++学习笔记

C++基础的学习笔记,部分内容只含有概念,后续会抽出时间陆续补充具体的实例代码,如果对内容有不同观点,欢迎各位私信探讨批评指正。

C++特点

● 在支持C语言的基础上,全面支持面向对象编程。

● 编程领域广泛,功能强大(最难的编程语言之一)

● C++语言的标准一直保持更新,本次课程以ISO C++98 、11标准为主。

● 为数不多支持底层操作的面向对象编程语言。

● 在面向对象编程语言中执行效率极高。

引用

引用从一定程度上讲是指针的平替。几乎被所有的面向对象编程语言所使用。引用相当于对某一目标变量起一个”别名“。操作引用与操作原变量完全一样。

int main()
{
     int a=1;//创建int类型的变量a赋值为1
     int &b=a;//引用b来给a起一个别名
     cout<<b<<" "<<a<<endl;//打印b a    打印结果为1 1
}

引用的注意事项:

1.可以改变引用的值,但是不能让引用改为其他变量的引用,加入用B给A起了别名这个时候

 对A操作和对B进行操作没有区别,但是不能用B再次给第二个变量 C起别名。

int main()
{

int A=1,C=2;
int &B=A;
B=C;//这样是赋值操作,并不是引用,B仍然是A的引用。
C++;

cout <<A<< <<&A<<endl;//打印值和地址    2 0x61fe88
cout <<B<< <<&B<<endl;//打印值和地址    2 0x61fe88
cout <<C<< <<&C<<endl;//打印值和地址    3 0x61fe84
return 0;
}
2.声明引用时,必须要初始化,并且初始化不能为null或者nullptr(c++11标准引入的一个特性类似于null,是一个特殊的指针字面量)。
初始化可以为数值,这样相当于给这个数值起了一个别名,但是不能再修改,需要用const修饰。
       
int main()

{
//int &a;//这样写就是错误的,没有初始化,编译时会报错
//int &b=null;//错误
//int &c=nullptr;//错误
const int &d=9;//常量引用
//d=54;//错错误
return 0;
}

3.可以对引用取地址赋值给指针

int main()
{
int a =1;
int &b=a;//这里的&符号是引用,在=左边是引用,在右边是取地址
int *c=&b;//这里的&符号是取地址

 cout << a << " " << &a << endl; // 1 0x61fe84
 cout << b << " " << &b << endl; // 1 0x61fe84
 cout << *c << " " << c << endl; // 1 0x61fe84
    
    return 0;
}
引用作为参数

引用作为参数进行定义的时候,在参数传递时,是不会产生副本的,这样会提高运行效率,在我们正常的编程中,建议使用引用作为传递参数。

引用形参,在不参数与计算的情况下,我们建议使用const关键字进行修饰。以达到引用的安全性。

函数

这里的函数粗略的讲解三种比较简单的函数知识:

内联函数

类似于C语言的宏定义,在编译过程种之间在函数体里展开,使用得当可以减少调用的开销

提高代码运行效率。

形式:在函数返回类型前加上inline。但是,手动添加关键字声明函数为内联函数后。也无法确定是否能真正为内联函数,具体是否被采纳,由编译器决定,关键字inline只是相当于给编译器提了个建议。

一般满足以下三个性质的建议写为内联函数:
1.代码长度不超过5行

2.不包含复杂的语法结构

3.频繁被调用

以我个人的理解,我把内联函数当成一个印章,就是要搭配印泥盖的印章,有了印章,很多文件就不需要动笔去签字,同样,有了内联函数,就不需要频繁的去在主函数里写重复的代码。

inline void A(string a)
{
    cout<<a<<endl;
}
int main()
{
    A("hello");
    return 0;
}

函数重载

函数重载,c++允许函数名相同,但是参数不能相同。这个参数不同可以是参数个数不同,参数类型不同,参数顺序不同......与返回值没关系。例:

int text(int a)
{
    return ++a;
}

 void text( string a)
{
    cout <<a<<endl;
}

void text(int a,string b)
{
    cout <<a<<" "<<b<<endl;
}

int main()
{
    text(5);       // 6 
    text("xixi");  // xixi
    text(5,"xixi");//5  xixi
    return 0;
}

上述代码有三个text函数但是是可以完美运行的,区别就在于三个函数的参数不同,主函数调用这三个text的时候,会根据传进来的参数分别调用符合格式的text函数。

哑元函数

哑元函数的形式是函数的参数里只有类型没有名称

void text(int)
{
    cout<<"哑元函数"<<endl;

}
int main()
{
    text();//哑元函数
    return 0;

}

作用有三个:1.区分函数重载,2.运算符重载(后文会用到)3.历史遗留,为了向上兼容

类与对象

类:类是一个抽象的概念,用于描述同一类对象的特点。

对象:根据类的概念所创造的实体。

这俩概念比较抽象,打个比方,以衣服举例,时至今日人们的衣服有多类型,比如上衣,裤子,袜子帽子......很多种,他们有共同的特点,比如都是穿在人身上的等等特点,因为这些特点它们统一被叫做“衣服”,那么这个“衣服”这个笼统的称呼就是一个类,而这个类下面又有很多类型,比如上衣,裤子等等,那么上衣,裤子也分别是一个类,就这样分下去,一直到你拿出一件具体的衣服,比如你现在的外套,这个时候,你的外套不再是指一个类而是一个具体的对象。

类和对象的关系:必须先有类再有对象,因为对象是根据类创建的,没有类就没有办法创建对象,类是对象的基础。类可以没有对象,但是这样的话,这个类就没有任何意义,一般没有这种写法。

类中最基础的内容包括两个部分,一个是属性,一个是行为。

● 属性:表示一些特征项的数值,比如说:身高、体重、年龄、性别、型号、颜色等等。而这些特征项的数值,也被称为”成员变量“。属性一般以名词存在。

● 行为:表示能执行的动作,能干什么事情?比如说:吃饭、睡觉、打架、爬、唱、跳、rap。飞、爆炸、接电话。也被称为”成员函数“。行为一般以动词存在。

成员 = 成员函数+成员变量。

类的关键词:class

对象

C++里对象的创建有两种方式:栈内存对象和堆内存对象。

 栈内存对象

对象所在的{}执行完毕后,自动被销毁

class A
{
   public://声明以下成员的权限为公有权限 是最开放的权限
        string str;//成员变量
        int num;//成员变量
    void aa()
    {
    cout<<"成员函数"<<endl;
    }
};
int main()
{
    A b;//以栈内存的方式创建对象
    b.str="haha";
    b.num=10;
    cout<<b.str<<endl;//haha
    cout<<b.num<<endl;//10
    b.aa();           //成员函数
    return 0;
}
栈内存对象

必须使用new关键字创建,使用指针保存,如果不适用delete关键字销毁,则堆内存对象会持续存在,从而导致内存泄漏。堆内存调用对象时,使用->而不是.

#include <iostream>

using namespace std;

// 大驼峰命名法/帕斯卡命名法
// 每个单词首字母要大写
class MobilePhone
{
public: // 权限:最开放的权限
    string brand;   // 品牌
    string model;   // 型号
    int weight;     // 重量

    void play_music()
    {
        cout << "只因你太美,哒哒哒,嘚嘚" << endl;
    }

    void run_game()
    {
        cout << "王者荣耀、原神、LOL、植物大战僵尸杂交版、DNF、4399、洛克王国、赛尔号" << endl;
    }

    void call()
    {
        cout << "110" << endl;
    }
};

int main()
{
    MobilePhone *mp = new MobilePhone;  // 堆内存对象
    mp->brand = "vivo";
    mp->model = "x100";
    mp->weight = 200;

    cout << mp->brand << " " << mp->model << " " << mp->weight << endl;

    mp->play_music();
    mp->run_game();
    mp->call();

    delete mp;  // 手动销毁

    mp = NULL;  // 建议指向空,防止后面误操作

    return 0;
}

构造函数

特点:

1.函数名与类名完全一样

2.构造函数不写返回值

3.不手动添加会自动添加默认无参构造函数

4.支持函数重载

5.支持函数参数默认值(全缺省默认构造函数)

6.拷贝构造函数:创建对象通过拷贝构造函数,不手写就自动添加。

#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;
}

浅拷贝:成员变量为指针类型,会导致两个对象成员变量指向同一处

#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;
}

深拷贝:需要手写构造函数,每次赋值都创建一个新副本,每个对象单独有自己的成员变量。缺陷:new开辟的空间无法释放,存在内存泄漏的问题。

#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(const Dog &d1)
    {
        name = new char[20];
        strcpy(name,d1.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;
}

析构函数

构造函数

析构函数

创建对象时手动调用

当对象销毁时,自动调用

函数名称是类名

函数名称是~类名

构造函数可以重载

析构函数没有参数,不能重载

用于创建对象时并初始化

用于销毁对象时释放资源

有返回值但是不写,返回值是新创建的对象

没有返回值

#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(const Dog &d1)
    {
        name = new char[20];
        strcpy(name,d1.name);
    }

    void show_name()
    {
        cout << name << endl;
    }

    ~Dog()
    {
       cout << "析构函数被调用了,name被销毁了" << endl;
       delete []name;   // [] 表示销毁的是数组
    }
};

int main()
{
    char arr[20] = "旺财";
    Dog d1(arr);
    Dog d2(d1); // 拷贝构造函数

    // 浅拷贝 更改外部内存,对象内部数据也被更改了,因为操作的是同一块内存
    strcpy(arr,"大黄");

    d1.show_name(); // 旺财
    d2.show_name(); // 旺财

    return 0;
}

作用域限定符::

用途:

名字空间

类内声明,类外定义

this指针

指向当前类对象首地址,成员函数中都有,this指针只能在类中使用,通常由编译器自动添加

用途:

1.类内调用成员

2.区分重名的成员变量和局部变量

3.链式调用:

返回值为当前类型的引用,return 类型为this

static

1.静态局部变量:第一次调用时创建,程序结束时销毁,同一类的对象共用这一份静态局部变量。

2.静态成员变量:需要类内声明类外初始化,程序运行时开辟内存空间,程序结束时销毁,同一类的对象共用一个静态成员变量,可以直接用类名调用,可以脱离对象使用。

3.静态成员函数:可以通过类名调用,可以脱离对象使用。没有this指针,不能调用其他非静态成员,但是可以调用静态成员。想要调用非静态成员:传参/直接在函数内创建一个对象

const

1.修饰局部变量:该变量不可改,适合不做运算的引用参数

2.修饰成员变量:常成员变量,该变量的值无法被修改,初始化方式:直接赋值/构造初始列表,两种都存在的情况下,由于先后初始化的顺序,第二种会把第一种覆盖掉,得到的值是第二种传进来的值。

3.修饰成员函数:常成员函数,可以调用成员变量,但是不能修改成员变量的值,不能调用非const 修饰的值,建议不修改值的函数都用const修饰,提高代码的稳定性

4.修饰对象:常量对象,对象的成员变量无法被修改,无法调用非const修饰的成员函数

友元

需要频繁读写类的数据成员时使用,参数传递,类型检查,安全性检查都要时间,会影响程序运行效率,但是破坏了类的封装性和隐藏性,稳定性变差

实现方式:

友元函数:类外的普通函数,需要在类内声明,并用friend修饰,能访问所有成员

没有this指针,声明时可以放在任意权限下,在多个类中声明则可以访问多个类

友元类:将一个类在另一个类中声明并且用friend修饰,

友元关系不能被继承,友元关系不具有交换性,互为友元需要类内声明类外实现

友元成员函数:一个类中的成员函数在另一个类中声明

运算符重载

不能重载的:三目运算符 ?: 成员运算符 . 指针运算符 * 作用域限定符:: sizeof

实现方式:

友元函数运算符重载:需要friend声明

成员函数运算符重载:比友元函数运算符重载少一个参数,被this指针代替了

特殊运算符重载:

赋值运算符重载:不手写就自动添加,只能由成员函数实现

类型转换运算符重载:只能由成员函数实现

模板

可以让类或函数支持一种通用的数据类型,关键字:templata /typename/class

函数模板/类模板:使函数/类支持模板编程,使函数/类支持通用的数据类型。

容器

用STL标准模板类实现,用来存储数据的集合,数据元素可以是任何类型,使用时需要引用头文件。

分类:

顺序容器:

array数组:特点定长,没法伸缩

vector向量:内部由数组实现,适合随机读写,不适合插入和删除

list列表:内部由双向循环链表实现,内存空间不连续,不支持下标。可以高效的删除和添加,不适合随机存取

deque队列:几乎支持所有vector的API接口

关联容器:元素之间没有严格的顺序,常见的是map键值对,键具有唯一性,通过键找到对应的值

迭代器:特殊的指针,用于容器元素的遍历和读写,两种:只读const_iterator和读写iterator

继承

面向对象的三大特征之一,代码复用。在一个类的基础上创建一个新的类, 已存在的类被称为“基类”或者“父类”。新建立的类被称为“派生类”或者“子类”。

派生类会与基类有差异化,体现在两个方面:

1.修改继承来的基类内容

函数隐藏:派生类创建一个与基类成员函数同名同参的函数,隐藏基类的函数。

属性更改:公有属性可以直接更改,更改后基类中的属性也会改变,因为改的是同一份变量。私有属性,需要使用基类提供的公有函数进行更改

2.新增派生类的内容

基类和派生类是相对的。一个类可能存在又是基类又是派生类的情况,取决于那两个类进行比较。

构造函数

构造函数与析构函数不能被继承。派生类的任意一个构造函数,都必须直接或者间接调用基类的任意一个构造函数。

解决方法:

1.补充基类的无参构造函数

2.手动在派生类中调用基类的构造函数

透传构造 :Son(string fn):Father(fn){}

委托构造:一个构造函数调用同类的另一个构造函数,性能低于透传构造,但代码维护性更好,参数最多的构造函数为被委托的构造函数。

继承构造:加一句话,自动添加父类的所有构造函数 using Father::Father;

对象创建和销毁流程

● 静态的创建,在程序开始运行就创建完成

● 在创建的过程中,头类型的内存区域基类先开辟(创建顺序:先基后派)(销毁顺序:先派后基)

● 以”对象执行中“为轴上下对称。

多重继承

一个派生类可以有多个基类。派生类对于每个基类的关系仍然可以看作是一个单继承

可能出现的问题:

重名问题 ,解决方式:使用基类的类名::方式调用

菱形继承:当一个派生类有多个基类,且这些基类又有一个共同的基类时,就会出现二义性问题,解决办法:

1.基类的类名::方式调用

2.虚继承,在基类中产生一张虚基类表,不占用任何对象的存储空间,由基类持有,程序加载时,表记录基类函数的调用地址偏移,派生类对象会出现一个隐藏的成员变量指针,指向虚基类表,占用四个字节,虚继承

权限

公有继承,派生类可以继承基类的成员,但是不可以访问基类的私有成员,基类的所有成员在派生类中权限不变。

保护继承,派生类可以继承基类的成员,但是不可以访问基类的私有成员,基类的公有成员与保护成员在派生类中都是保护权限

私有继承,派生类可以继承基类的成员,但是不能直接访问基类的私有成员。基类的公有成员与保护成员在派生类中都是私有权限

多态

静态多态:在编译时就能确定要调用的方法,通过函数重载和运算符重载、模板来实现。发生在编译时,因为在编译阶段编译器就能确定要调用的函数。

动态多态:在运行时根据对象的实际类型来确定要调用的函数,通过继承和函数覆盖来实现。发生在运行时,因为具体调用那个函数是在程序运行时根据对象的实际类型确定的。

多态“一种接口,多个状态”编写一个函数接口,根据传入的参数类型,执行不同的策略代码。

前提条件:公有继承,函数覆盖,基类的指针/引用指向派生类的对象

优点:代码灵活性,可拓展性,可维护性。使得代码更具通用性,减少重复代码

缺点:代码复杂,不易读,运行效率低,代码维护困难

函数覆盖

与函数隐藏的区别:

函数隐藏是派生类中存在与基类同名同参的函数,编译器会将基类的同名同参数的函数进行隐藏。不支持多态

函数覆盖是基类中定义了一个虚函数,派生类编译写一个同名同参数的函数将基类中的虚函数进行重写并覆盖。注意:覆盖的函数必须是虚函数。

虚函数

virtual关键字修饰,虚函数是函数覆盖的前提,函数名为斜体,

可以使用override检验函数覆盖是否成功

只有普通成员函数与析构函数可以被声明为虚函数

虚函数具有传递性,基类中被覆盖的函数是虚函数,派生类中新覆盖的函数也是虚函数

为什么要基类的指针/引用指向派生类的对象?

● 实现运行时多态:当使用基类的指针或引用指向派生类的对象时,程序在运行时会根据对象的实际类型来调用相应的函数,而不是根据指针或者引用类型。

● 统一接口:基类的指针可以作为一个通用的接口,用于操作不同类型的派生类对象,这样可以使代码更灵活,减少重复的代码。并且的支持和拓展更好进行维护。

多态实现原理

有虚函数的类会拥有一张虚函数表,类的对象内部拥有一个隐藏的虚函数表指针成员变量,指向当前类的虚函数表。

(Animal为基类,Dog 和Cat为派生类)

派生类创建时也会创建一个类似的虚函数表,不同的是,表中的虚函数会有所不同,在派生类中重新修改的虚函数会覆盖基类中的虚函数。

实现流程:

在代码运行时,通过对象的虚函数表指针找到虚函数表,在表中定位到虚函数的调用地址,从而执行对应虚函数内容。

虚析构函数

使用方法:在基类析构函数前用 virtual修饰

作用:使用指针指向派生类对象的方式申请的资源无法用delete销毁,调用析构函数只能触发基类中的析构函数,管不了派生类的死活,内存无法释放,出现内存泄漏问题。在基类析构函数加了virtual关键字后,传递性会把派生类的析构函数也变成虚析构函数,从而解决问题。

类型转换

解决内存泄漏的第二个方法。

C++11中不建议用c语言的类型转换方式例如

Animal *a1 = new Dog;

Dog *d = (Dog*)a1;

这样做的后果是有可能会带来一些安全隐患,让错误隐藏起来难以发现。

在C++11中提供了封装函数进行强制转换。

● static_cast(静态转换)

● dynamic_cast(动态转换)

● const_cast(常量转换)

● reinterpret_cast(重解释转换)

四个类型转换封装

static_cast(静态转换)

基本数据类型之间的转换

int x =1;

double y =static_cast<double>(x);//int 转double

特点:不会进行类型检查,需要手动判断是否复合规范

static_cast也可以用于类层次的转换中,即基类和派生类指针或者引用之间的转换。

● static_cast进行上行转换是安全的,即把派生类的指针或者引用转换为基类的。

● static_cast进行下行转换是不安全的,即把基类的指针或者引用转换为派生类的。

与C语言相比:

● static_cast的表达式更清晰,方便管理。

● static_cast会在编译时进行类型检查。

static_cast也可以转换自定义类型,但是目标类型必须含函数有对应参数的构造函数

dynamic_cast(动态转换)

dynamic_cast主要用于类层次之间的上行与下行转换。

在进行上行转换时,dynamic_cast与static_cast效果相同。但是进行下行转换时,dynamic_cast会比static_cast更加安全

const_cast

const_cast可以添加或者移除对象的const限定符。

主要用于改变指针或者引用的const效果,以便于在一定的情况下修改原本被声明为常量的对象,应该避免使用const_cast,而是考虑通过设计良好的接口或者其他正常手段避免需要进行此种转换

reinterpret_cast

t可以把内存里的值重新解释,这种转换风险极高,慎用!

抽象类

如果基类只表达一些抽象的概念,并不与实际的对象相关联,这时候就可以使用抽象类了。

如果一个类中有纯虚函数,则这个类就是一个抽象类。

如果一个类是抽象类,则这个类中一定有纯虚函数。

纯虚函数是虚函数的一种,这种函数只有声明,没有定义。

应用形式:virtual 返回值类型 函数名称(参数列表)=0;

不能把抽象类当作声明类型来用,不存在抽象类类型的对象。

抽象类作为基类的时候会有两种情况产生:
1.派生类继承抽象类,覆盖并且实现所有的纯虚函数,这个时候派生类不再是抽象类,因为类里不含有纯虚函数,是普通类。

2.派生类继承了抽象类后没有把纯虚函数覆盖并实现,此时派生类继承来的函数里还是含有纯虚函数,当然也是一个抽象类。

纯虚析构函数

本质上是析构函数,用来回收各个类开辟的空间资源,不能被继承。

实现条件:
必须为纯虚析构函数提供一个函数体。

需要在类外实现。

私有析构函数

即析构函数私有化,把析构函数设置为private私有权限

析构函数私有化后会有两种情况出现:
1.只能用new创建,但是无法正常用delete销毁

2.外部的栈内存对象无法创建

作用:
保护对象,如果需要一个对象一直存在的时候,可以使用这种写法,避免调用delete销毁对象。

异常处理

异常就是在程序运行期间产生的问题,例如string 类型.at()形式的下标范围越界

异常提供了一种转移控制权的方式,如果出现没有处理的异常就会造成程序运行崩溃。

对于异常有两种处理方式:

抛出异常(throw)

捕获异常(try-catch)

抛出异常(throw)

在代码块任意位置把异常抛出。

throw可以是任意的表达式,表达式结果的类型决定抛出的异常的类型。

异常抛出是抛出到函数调用的上一级。

捕获异常(try-catch)

在try代码块里执行代码,后面跟catch尝试捕获try代码块里的异常抛出。没有异常抛出,程序会继续运行。

catch捕获的异常类型要与try里抛出的异常类型一致,否则捕获失败仍然会程序崩溃

标准异常体系

C++给常见的异常类型进行了定义和分类,引入#include<stdexcept>头文件后可以使用。这个体系还是太薄弱,因此可以对其进行拓展。

自定义一个类型,继承自某个异常类型即可。

catch块可以匹配基类异常类型,提高匹配成功率,但是会降低匹配精度。

智能指针

堆内存对象需要手动使用delete销毁,否则会造成内存泄漏。而使用智能指针可以让堆内存对象具有栈内存的特性。原理是给需要手动回收堆内存对象上套上一个栈内存对象的模板类对象。头文件#include<memory>

智能指针有四种:

● auto_ptr(自动指针)(C++98 已废弃)

● unique_ptr(唯一指针)(C++11)

● shared_ptr(共享指针)(C++11)

● weak_ptr(虚指针)(C++11)

四个智能指针

auto_ptr(自动指针)

使用方式

:Test *t1 =newTest("A");

// 创建一个栈内存智能指针对象ap1

auto_ptr<Test>ap1(t1);// ap1管理t1

//auto_ptr<类名>指针名(对象名)

由于成员变量存在指针类型,因此拷贝构造函数与赋值运算符的使用会出现问题。与浅拷贝的问题不同的是,auto_ptr的复制语义会引起资源控制权转移的问题。

unique_ptr

对auto_ptr的改进,unique_ptr对其他持有的资源对象具有唯一控制权,不可通过常规的复制语法转移或者拷贝资源对象的控制器,可以通过特殊的语法实现控制权转移的效果

share_ptr

unique_ptr对资源具有独占性,多个share_ptr对象可以共享资源。

share_ptr有两种创建方式。

// 传统方式:使用new创建资源对象,再绑定ap1与资源

shared_ptr<Test>sp1(newTest("A"));

// 新方式:创建绑定一步到位

shared_ptr<Test>sp2=make_shared<Test>("B");

weak_ptr

weak_ptr是一个不控制资源对象的智能指针,也不会影响资源的引用计数,其主要的目的是协助shared_ptr工作。

通过weak_ptr的构造函数,参数传入一个持有资源对象的shared_ptr对象或者weak_ptr对象,即可创建。

weak_ptr与对象呈现弱相关性,因此不支持get等函数直接操作资源对象。

建议weak_ptr对象调用lock函数之前,先检测引用计数是否大于0,或者使用expired()检查是否可以转换为shared_ptr.

  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值