包括以下内容:
智能指针
auto_ptr
定义在memory头文件中,接受一个类型参数的模板,为动态分配的对象提供异常安全。
auto_ptr
不能管理动态分配的数组,所以不能将auto_ptr
存储在标准库容器类型中;当
auto_ptr
对象超出作用域或者另外撤销的时候,就自动回收auto_ptr
所指向的动态分配对象;auto_ptr
可以在new
和delete
之间发生异常时回收内存;auto_ptr
可以保持任何类型;不能直接将一个地址赋值给
auto_ptr
对象;接受指针的构造函数为
explicit
构造函数。
初始化:
因为构造函数为explicit
,所以必须使用初始化的直接形式来创建auto_ptr
对象:
auto_ptr<int> pi = new int(1024);//错误,无法自动完成int到auto_ptr的转换
auto_ptr<int> pi(new int(1024));//正确,直接用new初始化
复制和赋值:
auto_ptr
的复制和赋值改变右操作数,即在复制或赋值时把基础对象的所有权从原来的auto_ptr
对象转给副本,原来的auto_ptr
对象重置为未绑定。
赋值会删除左操作数指向的对象,例:
auto_ptr<int> ap1(new int(1));
auto_ptr<int> ap2(new int(2));
ap1 = ap2;
将ap2
赋值给ap1
后:
删除了
ap1
所指向的对象;将
ap1
置为指向ap2
所指的对象;ap2
是未绑定的auto_ptr
对象。
测试auto_ptr
对象:
如果不给定初始值,auto_ptr
对象是未绑定的,默认情况下,auto_ptr
的内部指针值为0
。
为了检查auto_ptr
是否绑定,不能直接测试,要用get
成员.
auto_ptr<int> ptr; //未绑定
if(ptr.get()) //用get来判断,未绑定返回0
*ptr = 2;
else
ptr.reset(new int (3)); //只能用reset重设
//要复位auto_ptr对象可以将0传给reset函数
缺陷:
要正确使用auto_ptr
类,必须坚持该类强加的下列限制:
不要使用
auto_ptr
对象保存指向静态分配对象的指针,否则,当auto_ptr
对象本身被销毁的时候,它将试图删除指向非动态分配对象的指针,导致未定义的行为;不要使用两个
auto_ptr
对象指向同一对象,不要使用同一指针来初始化或者reset
两个不同的auto_ptr
对象,不要使用一个auto_ptr
对象的ge
t函数的结果来初始化或者reset
另一个auto_ptr
对象。不要使用
auto_ptr
对象保存指向动态分配数组的指针,因为auto_ptr
对象被删除时调用的是普通delete
操作符,而不是数组的delete[]
操作符;不要讲
auto_ptr
对象存储在容器中。
has-a设计与is-a设计
has-a:基于用法(即引用)而不是继承,表示组合,包含关系。一个对象包含另一个对象
is-a:基于类继承或接口实现,示的是属于关系。具体类是接口的一种实现。
is-a是继承,has-a是组合。
auto关键字
作用: C++ 11中引入的auto
主要有两种用途:自动类型推断和返回值占位。
auto
声明的变量必须被初始化,以使编译器能够从其初始化表达式中推导出其类型。这个意义上,auto
并非一种类型声明,而是一个类型声明时的占位符。
auto
推导的一个最大的优势在于拥有初始化表达式的复杂类型变量声明时简化代码
可以避免类型声明时的麻烦而且避免类型声明时的错误
auto
与模板一起使用时,其“自适应”特性能够加强C++中泛型的能力
Tips:
用
auto
声明的变量必须初始化;auto
不能与其他类型组合连用;函数和模板参数不能被声明为
auto
;auto
是一个占位符,并不是一个他自己的类型,因此不能用于类型转换或其他一些操作;auto
会退化成指向数组的指针,除非被声明为引用。int a[9]; auto j = a; cout << typeid(j).name() << endl; // This will print int* auto& k = a; cout << typeid(k).name() << endl; // This will print int [9]
auto
返回值占位,主要与decltype
配合使用,用于返回值类型后置时的占位。
template <class T, class U>
auto Multiply(T t, U u)->decltype(t*u)
{
typedef decltype(t*u) NewType;
NewType *pResult = NewType(t*u);
return *pResult;
}
至于为什么需要将返回值类型后置,这里简单说明一下。如果没有后置,则函数声明为decltype(t*u) Multiply(T t, U u)
,但此时模板参数t和u还未声明,编译无法通
过。另外,如果非要使用返回值类型前置的形式,也可以将函数声明为decltype((*(T *)0)*(*(U *)0)) Multiply(T t, U u)
,但这种形式比较晦涩难懂,因此不推荐采用。
参考:
http://www.cnblogs.com/hujian/archive/2012/02/15/2352050.html
volatile关键字
volatile
的本意是易变的,volatile
关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。
遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
当要求使用volatile
声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被寄存。
一个定义为volatile
的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile
变量的几个例子:
并行设备的硬件寄存器(如:状态寄存器)
一个中断服务子程序中会访问到的非自动变量(
Non-automatic variables
)多线程应用中被几个任务共享的变量
问题:
一个参数既可以是
const
还可以是volatile
吗?解释为什么。
是的。一个例子是只读的状态寄存器。它是volatile
因为它可能被意想不到地改变。它是const
因为程序不应该试图去修改它。一个指针可以是
volatile
吗?解释为什么。
是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。下面的函数有什么错误?
int square(volatile int *ptr) { return *ptr * *ptr; }
这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; }
由于
*ptr
的值可能被意想不到地该变,因此a
和b
可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:long square(volatile int *ptr) { int a; a = *ptr; return a * a; }
参考:
mutable关键字
mutalbe
的中文意思是“可变的,易变的”,跟constant
(既C++
中的const
)是反义词。
在C++
中,mutable
也是为了突破const
的限制而设置的。被mutable
修饰的变量(mutable
只能由于修饰类的非静态数据成员),将永远处于可变的状态,即使在一个const
函数中。
假如类的成员函数不会改变对象的状态,那么这个成员函数一般会声明为const
。但是,有些时候,我们需要在const
的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe
来修饰。
参考:
本文来自我的独立博客,欢迎访问。