Effective c++ 读书总结 16-20
条款十六:成对使用new和delete时要使用相同的形式
1、在使用new操作符时,事件上做了两件事,一是申请堆区空间,二是在此空间上执行申请类型的构造函数。使用delete也是两件事,一是调用对象的析构函数,释放里面的资源,二是释放内存。
2、数组类型的空间申请和释放
string* strs = new string[10]; //new操作符会对每一个string对象都调用一次构造。
delete strs; //只会调用一次析构函数,这会导致string中许多需要在析构函数中释放的资源没有被释放。
string* str = new string; //构造一个string对象
delete[] str; //未有定义,编译器之所以知道对一块空间内存在了多少对象,需要调用多少次析构,
//是因为它可能在某个位置存储了数组的长度信息,如果就这样删除,
//编译器很有可能会将string对象的一部分读取成数组的长度,而后进行析构的调用。
3、正确使用数组
可以使用stl提供的容器vector来代替手动申请数组,如果真的需要,记得new,delete new[],delete[]
条款十七:以独立语句将newed对象置入智能指针中
1、为什么
以独立的语句将newed对象置入智能指针,目的就是防止在将对象加入智能指针之前有异常发生,那么刚刚new的对象就会丢失,也就是说在智能指针析构时不会释放申请的对象空间,造成内存泄漏。
2、容易忽略的情况
void test(shared_ptr<TEST> ptr, int x);
test(shared_ptr<TEST>(new TEST),cal());
//c++不保证参数的执行顺序,也就是参数计算的顺序未知,如果1.new TEST; 2.cal(); 3.shared_ptr()
//那么在这个过程中,如果cal()发生异常,就会导致newed对象没有被装入智能指针中,资源就不会在智能指针
//引用计数为0时释放
条款十八:让接口容易被正确使用,不容易被误用
1、促进接口正确使用的方法,包括保证接口的一致性,以及与内置类型的兼容。
2、阻止接口误用的方法,创建参数新类型,限制类型操作,束缚对象的值,以及消除用户的资源管理责任。
有时函数接口可能需要返回一个指针类型,但是这就需要调用者在外部释放,而经常被调用者忘记,可能通过返回智能指针的方式,把资源释放的责任交给智能指针,也可能让用户将需要操作的内存空间以引用的形式传递进来,这样用户可能不会忘记释放内存。
条款十九:设计class犹如设计type
1、设计一个class要考虑很多问题
新类型对象的构造和析构行为,对象的初始化和赋值有哪些区别,对象的拷贝是否可以使用默认的拷贝构造和赋值运算符,新类型是否需要继承自那个父类,是否要实现继承而来的virtual函数,哪些操作符合函数对新类型是合理的。。。
条款二十:宁以pass-by-reference-to-const替换pass-by-value
1、两种参数传递
void test(const string& str);
void test(string str);
第一种方式使用引用,也就是参数str与外部对象共同指向同一块空间,不需要对象的拷贝构造,在函数控制块结束后也不会对对象进行析构。对象的拷贝构造和析构很可能有大量的工作要完成,这十分浪费效率。
第二种方式使用值传递,那要经过拷贝构造和对象的析构。
第二种方式在函数内部对对象的修改不会改变调用函数时传递的对象的值,因为函数内部的只是一个副本,所以要替代值传递要使用const引用的形式。
2、容易出错的情况
class Base
{
public:
virtual ~Base(){}
virtual void print() const
{
cout<<"print base"<<endl;
}
};
class Derived : Base
{
public:
virtual void print() const
{
cout<<"print derived"<<endl;
}
};
void test1(Base b){
b.print();
}
void test2(const Base& b){
b.print();
}
int main()
{
Derived d;
test1(d);
test2(d);
return 0;
}
像这样当调用test1时,因为使用的是值传递,那么就是以d的父类部分拷贝构造参数b,这样当在函数内部调用print函数时,输出的其实是"print base"。但这与我们所期待的并不一致。
然而当调用test2时,使用引用传递,就会以d的父类部分起别名传递给函数,其实指向的是父类部分(同一块空间),所以输出内容则是"print derived"。
3、使用场景
当类型的构造和析构的影响效率时可以使用const TYPE&
引用传递其实是以指针的方式实现的,只是在函数内的所以操作都是对指针间接引用操作,所以对于一些内置类型来说,使用值传递要比引用传递更加高效。
另外不要因为一个类型对象的大小来决定使用哪种方式,因为类型虽然小的对象内部也可能包含的大量的资源,构造和析构需要执行大量操作。
下一篇: JavaScript第五章对象
推荐阅读