一、auto关键字
auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型。
int a = 0;
auto b = a;//b自动成为int类型
其自动类型推断发生在编译期,不会造成运行时效率的降低。
使用注意:
1、与const关键字类似,auto变量必须在定义时初始化。
2、定义在一个auto序列内的变量必须推导为同一个类型。
auto a4 = 10, a5 = 20, a6 = 30;//正确
auto b4 = 10, b5 = 20.0, b6 = 'a';//错误,没有推导为同一类型
3、当auto的初始值是引用和常量时,它总能看破”内在类型“。auto会忽略初始值的引用,也就是说当初始值是一个引用时,auto推导出来的只会是去掉引用之后的类型。如果初始化表达式为const或volatile(或者两者兼有),则除去const/volatile语义。
const int a1 = 10;
auto b1= a1; //b1的类型为int而非const int(去除const)
const auto c1 = a1;//此时c1的类型为const int
b1 = 100;//合法
c1 = 100;//非法
但是如果auto关键字带上&号,则不去除const语意。
const int a2 = 10;
auto &b2 = a2;//因为auto带上&,故不去除const,b2类型为const int
b2 = 10; //非法
二、nullptr关键字
在C++11标准中,推荐使用nullptr关键字代替NULL。和NULL相比,它可以转换成任何指针类型和bool类型,但是不可以被转换成int。
char *p1 = nullptr; // √
int *p2 = nullptr; // √
bool b = nullptr; // √
int a = nullptr; // ×
三、智能指针
常常会有这样一个问题:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。智能指针的大小为裸指针的大小。
所有的智能指针类(包括 std::unique_ptr)均包含于头文件中。
1、std::auto_ptr(已被废弃)
基本用法:
std::auto_ptr<int> p1(new int(10));
std::auto_ptr<int> p2;
p2.reset(new int(10));
两个智能指针均持有一个在堆上分配的值为10的 int 对象,这两块堆内存均可以在 sp1 和 sp2 释放时得到释放。
复制语义:
复制一个 std::auto_ptr 对象时(拷贝复制或 operator = 复制),原对象所持有的堆内存对象也会转移给复制出来的对象,此时原来的对象持有的堆内存将不存在,此时就会有潜在的内存崩溃问题。
2、std::unique_ptr
为了保证同一时间内只有一个智能指针可以指向该对象,std::unique_ptr 对其持有的堆内存具有唯一拥有权,引用计数永远是 1,std::unique_ptr 对象销毁时会释放其持有的堆内存。同时std::unique_ptr禁止复制语义(与std::auto_ptr区别)。
std::unique_ptr<int> sp1(std::make_unique<int>(123));
//以下代码无法通过编译
//std::unique_ptr<int> sp2(sp1);
std::unique_ptr<int> sp3;
//以下代码无法通过编译
//sp3 = sp1;
但是可以将 std::unique_ptr通过函数返回。
移动构造:将一个 std::unique_ptr 对象持有的堆内存转移给另外一个。
std::unique_ptr<int> sp1(std::make_unique<int>(123));
std::unique_ptr<int> sp2(std::move(sp1));
std::unique_ptr<int> sp3;
sp3 = std::move(sp2);
sp1->sp2->sp3,sp1 和 sp2 不再持有堆内存的引用,变成一个空的智能指针对象。
3、std::shared_ptr
td::unique_ptr 对其持有的资源具有独占性,而 std::shared_ptr 持有的资源可以在多个 std::shared_ptr 之间共享,每多一个 std::shared_ptr 对资源的引用,资源引用计数将增加 1,每一个指向该资源的 std::shared_ptr 对象析构时,资源引用计数减 1,最后一个 std::shared_ptr 对象析构时,发现资源计数为 0,将释放其持有的资源。
//初始化方式1
std::shared_ptr<int> sp1(new int(123));
//初始化方式2
std::shared_ptr<int> sp2;
sp2.reset(new int(123));
//初始化方式3
std::shared_ptr<int> sp3;
sp3 = std::make_shared<int>(123);
注意:智能指针管理的是堆对象,栈对象会在函数调用结束后自行销毁,因此不能通过 shared_from_this() 将栈对象交由智能指针对象管理。
shared_ptr在相互引用时,会发生死锁问题。两个std::shared_ptr 相互引用,那么这两个指针的引用计数永远不可能下降为 0, 资源永远不会释放。
4、std::weak_ptr
std::weak_ptr 是一个不控制资源生命周期的智能指针,是对对象的一种弱引用,只是提供了对其管理的资源的一个访问手段,引入它的目的为协助 std::shared_ptr 工作。
//创建一个std::shared_ptr对象
std::shared_ptr<int> sp1(new int(123));
std::cout << "use count: " << sp1.use_count() << std::endl;
//通过构造函数得到一个std::weak_ptr对象
std::weak_ptr<int> sp2(sp1);
std::cout << "use count: " << sp1.use_count() << std::endl;
//通过赋值运算符得到一个std::weak_ptr对象
std::weak_ptr<int> sp3 = sp1;
std::cout << "use count: " << sp1.use_count() << std::endl;
//通过一个std::weak_ptr对象得到另外一个std::weak_ptr对象
std::weak_ptr<int> sp4 = sp2;
std::cout << "use count: " << sp1.use_count() << std::endl;
创建 std::weak_ptr时 ,不会增加资源的引用计数,引用计数的值总是 1。
四、初始化列表
初始化类的成员变量有两种方式:构造函数、初始化列表。对于类类型来说,使用初始化列表少了一次调用默认构造函数的过程,提高了效率。
以下几种情况必须使用初始化列表:
1、常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
2、引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
3、没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。
五、右值引用
左值和右值的区别:前者能取地址,后者不可以。所有的具名变量或对象都是左值,而匿名变量则是右值:
int a = 0;
0为右值,a为左值。
对右值进行引用,可以使得右值在离开表达式时仍然存在。
T&& k = getVar();
T&& t在发生自动类型推断的时候,它是未定的引用类型(universal references),如果被一个左值初始化,它就是一个左值;如果它被一个右值初始化,它就是一个右值,它是左值还是右值取决于它的初始化。
当一个类中的成员变量有指针时,需要深拷贝为指针成员分配新的内存并进行内容拷贝,从而避免悬挂指针的问题。为了提高性能,使用移动构造函数接收一个右值引用作为参数,使用右值引用的参数初始化其指针成员变量。
HasPtrNum(HasPtrNum && h):d(h.d){//移动构造函数
h.d = nullptr; //将临时值的指针成员置空
}
其原理就是使用在构造对象a时,使用h.d来初始化a,然后将临时对象h的成员变量d指向nullptr,从而保证临时变量析构时不会释放对内存。
295

被折叠的 条评论
为什么被折叠?



