c++高阶语法(一)

前面已经有所有的c++语法都是属于比较基础的c++语法,后面博主会分析一些c++比较高阶的语法给大家,提高对于c++的理解

一)智能指针:

智能指针就是帮我们C++程序员管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏!我们知道分配内存后,我们要进行释放,当内存没有被释放,内存会泄露,造成不好影响。

于是,智能指针就是来解决指针自动释放的问题。下面我们一起来看看智能指针的相关内容吧。

一,auto_ptr

auto_ptr 是c++ 98定义的智能指针模板,其定义了管理指针的对象,可以将new 获得(直接或间接)的地址赋给这种对象。当对象过期时,其析构函数将使用delete 来释放内存!

用法:
头文件: #include < memory >
用 法: auto_ptr<类型> 变量名(new 类型)

例子:

class Test {
public:
    Test() { cout << "Test的构造函数..." << endl; }
    ~Test() { cout << "Test的析构函数..." << endl; }

int getDebug() { return this->debug; }

private:
    int debug = 20;
};

int main(void) {

    //Test *test = new Test;//没有定义智能指针的情况,这样其实内存泄露了,不会打印析构函数的内容的
    auto_ptr<Test> test(new Test);//这里也就定义了智能指针,这里就会打印构造函数和析构函数的内容

    cout << "test->debug:" << test->getDebug() << endl;
    cout << "(*test).debug:" << (*test).getDebug() << endl;

    return 0;

智能指针的三个常用函数:

1.get() 获取智能指针托管的指针地址

// 定义智能指针

auto_ptr<Test> test(new Test);

Test *tmp = test.get(); // 获取指针返回

cout << "tmp->debug:" << tmp->getDebug() << endl;

但我们一般不会这样使用,因为都可以直接使用智能指针去操作,除非有一些特殊情况。

2.release() 取消智能指针对动态内存的托管

// 定义智能指针

auto_ptr<Test> test(new Test);

Test *tmp2 = test.release(); // 取消智能指针对动态内存的托管

delete tmp2; // 之前分配的内存需要自己手动释放

也就是智能指针不再对该指针进行管理,改由管理员进行管理!

3.reset() 重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉

// 定义智能指针

auto_ptr<Test> test(new Test);

test.reset(); // 释放掉智能指针托管的指针内存,并将其置NULL

test.reset(new Test()); // 释放掉智能指针托管的指针内存,并将参数指针取代之

reset函数会将参数的指针(不指定则为NULL),与托管的指针比较,如果地址不一致,那么就会析构掉原来托管的指针,然后使用参数的指针替代之。然后智能指针就会托管参数的那个指针了。

不过,C++11后不建议使用auto_ptr,已使用unique_ptr替代。弊端这里就不详细说了,有兴趣的自己可以上网去了解一下

二.unique_ptr

unique_ptr 和 auto_ptr用法几乎一样,除了一些特殊。

unique_ptr特性

  1. 基于排他所有权模式:两个指针不能指向同一个资源
  2. 无法进行左值unique_ptr复制构造,也无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值
  3. 保存指向某个对象的指针,当它本身离开作用域时会自动释放它指向的对象。
  4. 在容器中保存指针是安全的

(1)无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值 

例子:

unique_ptr<string> p1(new string("I'm Li Ming!"));
unique_ptr<string> p2(new string("I'm age 22."));
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

p1 = p2;                    // 禁止左值赋值
unique_ptr<string> p3(p2);    // 禁止左值赋值构造

unique_ptr<string> p3(std::move(p1));
p1 = std::move(p2);    // 使用move把左值转成右值就可以赋值了,效果和auto_ptr赋值一样

cout << "p1 = p2 赋值后:" << endl;
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

(2)在 STL 容器中使用unique_ptr,不允许直接赋值

vector<unique_ptr<string>> vec;
unique_ptr<string> p3(new string("I'm P3"));
unique_ptr<string> p4(new string("I'm P4"));

vec.push_back(std::move(p3));
vec.push_back(std::move(p4));

cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;

vec[0] = vec[1];    /* 不允许直接赋值 */
vec[0] = std::move(vec[1]);        // 需要使用move修饰,使得程序员知道后果

cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;

(3)支持对象数组的内存管理

// 会自动调用delete [] 函数去释放内存

unique_ptr<int[]> array(new int[5]); // 支持这样定义

除此之外,,unique_ptr的其余用法都与auto_ptr用法一致。

1.构造。

// unique_ptr<T> up; 空的unique_ptr,可以指向类型为T的对象
unique_ptr<Test> t1;

// unique_ptr<T> up1(new T());    定义unique_ptr,同时指向类型为T的对象
unique_ptr<Test> t2(new Test);

// unique_ptr<T[]> up;    空的unique_ptr,可以指向类型为T[的数组对象
unique_ptr<int[]> t3;

// unique_ptr<T[]> up1(new T[]);    定义unique_ptr,同时指向类型为T的数组对象
unique_ptr<int[]> t4(new int[5]);

// unique_ptr<T, D> up();    空的unique_ptr,接受一个D类型的删除器D,使用D释放内存
unique_ptr<Test, DestructTest> t5;

// unique_ptr<T, D> up(new T());    定义unique_ptr,同时指向类型为T的对象,接受一个D类型的删除器D,使用删除器D来释放内存
unique_ptr<Test, DestructTest> t6(new Test)

2.赋值

unique_ptr<Test> t7(new Test);

unique_ptr<Test> t8(new Test);

t7 = std::move(t8); // 必须使用移动语义,结果,t7的内存释放,t8的内存交给t7管理

t7->doSomething();

3.主动释放对象

unique_ptr<Test> t9(new Test);

t9 = NULL;

t9 = nullptr;

t9.reset();//释放掉智能指针托管的指针内存,并将其置NULL

4.放弃对象的控制权

Test *t10 = t9.release();

5.重置

t9.reset(new Test);

三.shared_ptr

用法:1.引用计数的使用:调用use_count函数可以获得当前托管指针的引用计数。

shared_ptr<Person> sp1;

shared_ptr<Person> sp2(new Person(2));

// 获取智能指针管控的共享指针的数量    use_count():引用计数
cout << "sp1    use_count() = " << sp1.use_count() << endl;输入0
cout << "sp2    use_count() = " << sp2.use_count() << endl << endl;输出1

// 共享
sp1 = sp2;

cout << "sp1    use_count() = " << sp1.use_count() << endl;输入2
cout << "sp2    use_count() = " << sp2.use_count() << endl << endl;输出2

shared_ptr<Person> sp3(sp1);
cout << "sp1    use_count() = " << sp1.use_count() << endl;输出3
cout << "sp2    use_count() = " << sp2.use_count() << endl;输出3
cout << "sp2    use_count() = " << sp3.use_count() << endl << endl;输出3

如上代码,sp1 = sp2; 和 shared_ptr< Person > sp3(sp1);就是在使用引用计数了。

sp1 = sp2; --> sp1和sp2共同托管同一个指针,所以他们的引用计数为2;
shared_ptr< Person > sp3(sp1); --> sp1和sp2和sp3共同托管同一个指针,所以他们的引用计数为3;

2.几种构造

1). shared_ptr< T > sp1; 空的shared_ptr,可以指向类型为T的对象

shared_ptr<Person> sp1;

Person *person1 = new Person(1);

sp1.reset(person1); // 托管person1

2). shared_ptr< T > sp2(new T()); 定义shared_ptr,同时指向类型为T的对象

shared_ptr<Person> sp2(new Person(2));

shared_ptr<Person> sp3(sp1);

3). shared_ptr<T[]> sp4; 空的shared_ptr,可以指向类型为T[]的数组对象 C++17后支持

shared_ptr<Person[]> sp4;

4). shared_ptr<T[]> sp5(new T[] { … }); 指向类型为T的数组对象 C++17后支持

shared_ptr<Person[]> sp5(new Person[5] { 3, 4, 5, 6, 7 });

5). shared_ptr< T > sp6(NULL, D()); //空的shared_ptr,接受一个D类型的删除器,使用D释放内存

shared_ptr<Person> sp6(NULL, DestructPerson());

6). shared_ptr< T > sp7(new T(), D()); //定义shared_ptr,指向类型为T的对象,接受一个D类型的删除器,使用D删除器来释放内存

shared_ptr<Person> sp7(new Person(8), DestructPerson());

3.初始化

方式一:1.构造函数

shared_ptr<int> up1(new int(10)); // int(10) 的引用计数为1

shared_ptr<int> up2(up1); // 使用智能指针up1构造up2, 此时int(10) 引用计数为2

2.方式二:使用make_shared 初始化对象,分配内存效率更高(推荐使用)
make_shared函数的主要功能是在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr; 用法:make_shared<类型>(构造类型对象需要的参数列表);

shared_ptr<int> up3 = make_shared<int>(2); // 多个参数以逗号','隔开,最多接受十个
shared_ptr<string> up4 = make_shared<string>("字符串");
shared_ptr<Person> up5 = make_shared<Person>(9);

4.赋值

shared_ptrr<int> up1(new int(10));  // int(10) 的引用计数为1
shared_ptr<int> up2(new int(11));   // int(11) 的引用计数为1
up1 = up2;    // int(10) 的引用计数减1,计数归零内存释放,up2共享int(11)给up1, int(11)的引用计数为2,这种赋值方式是允许的哦

5.主动释放对象

shared_ptrr<int> up1(new int(10));

up1 = nullptr ; // int(10) 的引用计数减1,计数归零内存释放 // 或 up1 = NULL; // 作用同上

6.重置

p.reset() ; 将p重置为空指针,所管理对象引用计数 减1
p.reset(p1); 将p重置为p1(的值),p 管控的对象计数减1,p接管对p1指针的管控
p.reset(p1,d); 将p重置为p1(的值),p 管控的对象计数减1并使用d作为删除器
p1是一个指针!

7.交换

p1 和 p2 是智能指针

std::swap(p1,p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变

p1.swap(p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变

注意:使用shared_ptr智能指针时,要注意避免对象交叉使用智能指针的情况!

实例:

#include <iostream>
#include <string>
#include <memory>//不要忘了哦

using namespace std;

class Girl;

class Boy {
public:
    Boy() {
        cout << "Boy 构造函数" << endl;
    }

    ~Boy() {
        cout << "~Boy 析构函数" << endl;
    }

    void setGirlFriend(shared_ptr<Girl> _girlFriend) {
        this->girlFriend = _girlFriend;
    }

private:
    shared_ptr<Girl> girlFriend;//定义智能指针
};

class Girl {
public:
    Girl() {
        cout << "Girl 构造函数" << endl;
    }

    ~Girl() {
        cout << "~Girl 析构函数" << endl;
    }

    void setBoyFriend(shared_ptr<Boy> _boyFriend) {
        this->boyFriend = _boyFriend;
    }

private:
    shared_ptr<Boy> boyFriend;//定义智能指针
};


void useTrap() {
    shared_ptr<Boy> spBoy(new Boy());
    shared_ptr<Girl> spGirl(new Girl());

// 单方获得管理 //正解

spBoy->setGirlFriend(spGirl);

spGirl->setBoyFriend(spBoy);

    // 陷阱用法(错解)
    spBoy->setGirlFriend(spGirl);
    spGirl->setBoyFriend(spBoy);
    // 此时boy和girl的引用计数都是2
}


int main(void) {
    useTrap();

    system("pause");
    return 0;
}
四。weak_ptr

weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。 同时weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象。

1.弱指针的使用;
weak_ptr wpGirl_1; // 定义空的弱指针
weak_ptr wpGirl_2(spGirl); // 使用共享指针构造
wpGirl_1 = spGirl; // 允许共享指针赋值给弱指针

2.弱指针也可以获得引用计数;
wpGirl_1.use_count()

3.弱指针不支持 * 和 -> 对指针的访问;

4.在必要的使用可以转换成共享指针 lock();

如:shared_ptr<Girl> sp_girl;

sp_girl = wpGirl_1.lock();

// 使用完之后,再将共享指针置NULL即可

sp_girl = NULL;

注意weak_ptr指针的expired函数的用法。

expired:判断当前weak_ptr智能指针是否还有托管的对象,有则返回false,无则返回true

如果返回true,等价于 use_count() == 0,即已经没有托管的对象了;当然,可能还有析构函数进行释放内存,但此对象的析构已经临近(或可能已发生)。

最后说一下智能指针的使用陷阱:

1.不要把一个原生指针给多个智能指针管理;

int *x = new int(10);
unique_ptr< int > up1(x);
unique_ptr< int > up2(x);
// 警告! 以上代码使up1 up2指向同一个内存,非常危险或以下形式:
up1.reset(x);//up1 up2指向同一个内存
up2.reset(x);

2.记得使用u.release()的返回值;
在调用u.release()时是不会释放u所指的内存的,这时返回值就是对这块内存的唯一索引,如果没有使用这个返回值释放内存或是保存起来,这块内存就泄漏了.

3.禁止delete 智能指针get 函数返回的指针;
如果我们主动释放掉get 函数获得的指针,那么智能指针内部的指针就变成野指针了,析构时造成重复释放,带来严重后果!

4.禁止用任何类型智能指针get 函数返回的指针去初始化另外一个智能指针!
shared_ptr< int > sp1(new int(10));
// 一个典型的错误用法 shared_ptr< int > sp4(sp1.get());
最后,
智能指针虽然使用起来很方便,但是要注意使用智能指针的一些陷阱,否则会造成严重的内存报错或者内存泄露等问题!

本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值