习题首页
12.1.1 shared_ptr类
知识点1:静态内存用来保存局部static对象、类static数据成员以及定义在任何函数之外的变量。
知识点2:栈内存用来保存定义在函数内的非static对象。
知识点3:分配在栈或内存中的对象由编译器自动创建和销毁。
知识点4: 栈对象在其定义的程序运行时才存在,static对象在使用之前分配,在程序结束时销毁。
换句话说,静态变量和一般变量唯一的不同就是整个程序运行期间都将存在。她所在的内存不会被释放。
知识点5:程序用堆来存储动态分配的对象,动态对象的生存周期由程序来控制,当不在使用时,必须用代码显示的销毁。
知识点6:为了更安全的使用动态内存,新标准库定义了两种智能指针类型来管理动态对象,头文件为memory,智能指针的主要作用就是自动释放所指向的对象,shared_ptr类型允许多个指针指向同一对象,unique_ptr类型则独占所指向对象。
shared_ptr p1;//定义一个智能指针,指向对象为string类型
shared_ptr> p2;
知识点7:make_shared()函数:最安全的分配和使用动态内存的方法,此函数在动态内存中分配一个对象并初始化它,返回指向该对象的shared_ptr,头文件为memory。
shared_ptr p3 = make_shared(42);//p3为智能指针,指向一个值为42的对象
auto p3 = make_shared(42);//利用auto比较简便,若不传递任何参数,会值初始化
知识点8:每个shared_ptr都有一个关联的计数器,称为引用计数,个人理解就是该对象被引用的次数,拷贝情况下会递增:
1:用一个shared_ptr初始化另一个shared_ptr(拷贝)
2:将一个shared_ptr传递给一个函数当参数,值传递(拷贝)
3:作为函数的返回值,(返回的是自身的拷贝,也就是活引用的次数+1)
计数器递减情况:
1:给shared_ptr赋予一个新值(也就是说自身指向了另外一个地址,原来指向的对象已经没有引用者,则会自动释放)
2:一个shared_ptr离开其作用域时,会被销毁(递减)
当一个shared_ptr的计数器变为0,他就会自动释放自己所管理的对象,前提是其指向的对象只有一个引用者。
知识点9:使用动态内存的原因:让多个对象共享相同的底层数据。也就是说拷贝的情况虽然发生,但是并不是元素的拷贝,而是将本身的指向拷贝给另一个指针对象,让这一个对象也指向自己所指向的对象,这样在本身释放以后,还有另一个对象指向自身原来所指向的对象。
练习12.1
b2已经被销毁了,但b2中的元素还没有被销毁,所以b1和b2都包含4个元素。
练习12.2
直接在函数声明之后加const。
练习12.3
是否要定义为const版本取决于是否需要加以修改参数,这两个函数都不会对参数进行修改,所以无需加const。
练习12.4
因为data_size的类型为size_type,是一个无符号类型,即使是负数,也会自动转化为非负。
练习12.5
explicit的作用就是抑制构造函数的隐式转换
优点:不会自动的进行类型转换,必须清楚的知道类类型
缺点:必须用构造函数显示创建一个对象,不够方便简单
12.1.2
知识点1:C++语言定义了new和delete来分配和释放内存,但相对于智能指针,这两个运算符管理内存容易出错。
知识点2:在自由空间分配的内存是无名的,因此new无法为其分配对象命名,而是返回指向该对象的指针。
知识点3:默认情况下,new分配的对象是默认初始化的,这就说明内置类型或者组合类型将是为定义的(例如:int,会指向一个为初始化的int),类类型对象将用默认构造函数进行初始化(例如string,会产生一个空string)。
知识点4:建议使用值初始化(在最后加一对小括号即可),值初始化的内置类型对象有着良好定义的值,而默认初始化的对象的值是未定义的。
知识点5:auto来推断分配对象的类型时,只有单一初始化器时才能使用。利用new分配const对象是合法的。
auto p1 = new auto(obj); //p指向一个与obj类型相同的对象
auto p2 = new auto(a,b,c);//错误:括号内只能有单个初始化器
const int *pci = new const int(1024); //分配并初始化一个const int
知识点6:delete完成两个操作:销毁给定指针所指向的对象,释放对应的内存,delete的参数必须是指向动态分配的对象或是一个空指针。
知识点7:内置类型指针管理的动态内存在被显式的释放前一直都会存在,因为内置类型与类类型不同,虽然内置类型的指针会在离开作用域后被销毁,但是其内存依然存在
知识点8:同一块内存释放两次:两个内置类型的指针指向同一块自由空间分配的内存,在对一个指针进行delete之后,其指向的内存也会被释放,若再对第二个指针进行delete,会造成自由空间破坏
知识点9:忘记使用delete,使用已经释放掉的对象都是经常发生的(使用new和delete时),所以尽可能的使用智能指针
知识点10:在很多机器上,即使delete了某个内置类型的指针(也就是说释放了对应的内存空间),虽然指针已经无效,但是其仍然保留这释放空间的对应地址,变成了空悬指针,也就是说我们要保留指针,可以将其置为空指针
练习12.6
#include
#include
#include
using namespace std;
vector *create_vi() {
return new std::vector;
}
void push_vi(vector *p) {
int i;
while (cin >> i) {
p->push_back(i);
}
}
void print_vi(vector *p) {
for (const auto i : (*p)) {
cout << i << " ";
}
cout << endl;
}
int main() {
auto p = create_vi();
push_vi(p);
print_vi(p);
delete(p);
system("pause");
return 0;
}
练习12.7
#include
#include
#include
#include
using namespace std;
shared_ptr> create_vi() {
return make_shared>();
}
void push_vi(shared_ptr> p) {
int i;
while (cin >> i) {
p->push_back(i);
}
}
void print_vi(shared_ptr> p) {
for (const auto i : (*p)) {
cout << i << " ";
}
cout << endl;
}
int main() {
auto p = create_vi();
push_vi(p);
print_vi(p);
system("pause");
return 0;
}
练习12.8
p是一个内置类型的指针,返回p会使得p的类型转化为bool类型,其指向的动态内存空间将无法得到释放。
练习12.9
q,r为内置类型指针,保存动态内存的地址,进行二次赋值之后,r原来指向的内存空间将得不到释放,造成内存泄漏。
q2,r2为智能指针,r2进行赋值之后,其计数器将减一,由于r2是指向该内存空间的唯一智能指针,所以该内存会得到释放。