effective老笔记(二)

条款十二

使用一个类来管理你的资源(即RAII,获取即初始化)

在这个类的析构函数中释放管理的指针

条款十三

可以的话,使用shared_ptr<>和unique_ptr<>(不可复制/拷贝)来代替内置指针,复制RAII类的时候应当像这两个类一样:复制管理的底层资源->shared;或阻止拷贝,或将控制权转让,不过unique是阻止了拷贝复制。

条款十四

所有RAII类都应当有获取原始资源的方法,显示的get()函数较为安全,隐式的用户类型转换(operator type())则会比较方便。

RAII(资源获取即初始化resource acquisition is initialization)保证被管理的资源会被释放。

一个类管理的指针在构造时获取资源,析构的时候则释放资源,由于外层是一个类,故可以拥有c++的自动析构调用。

条款十五

若new时使用了new type[num]则delete时需要使用delete[](没有参数)

这一点在typedef / using定义一个数组的时候尤为重要,因为可能会注意不到使用的是一个数组的名字类型,所以干脆不要用typedef/using来定义一个新的数组的名字,使用vector等容器来代替会好的多。

条款十六

用一个独立的语句来创建你的智能指针,特别是当你需要传入智能指针到一个函数的时候

函数的实参的核算是不确定的,即两个参数不一定哪一个先完成构造

f( shared_ptr<T>(new T(),fun() )

在这样一个函数中

条款十七

设计易用但是难出错的接口

如果一个接口要求用户必须做某事,那么就是有“不正确使用”的倾向,因为用户很可能会忘记这么一件事。

条款十八

为了设计荣容易但是难以误用的接口需要:

“促进正确使用”:定义与内置类型行为就兼容,并且统一的接口。比如size()表示大小,并且所有这个继承体系中都用size(),功能概念上接近的也用size()

“防止误用”:创建新类型(代替值类型),限制类型上的操作(private构造、并用static函数来取得对象),束缚对象值(static返回正确的对象,即提供所有可能出现的值,而不是让用户来定义(如果狗少的话))、消除用户的资源管理责任(不返回内置指针,而是定义好了删除器的智能指针,籍此来把释放资源的责任交给系统RAII)

return shared_ptr<type>(指针,删除器)

定义删除器,而不是让用户来手动调用某个删除函数

智能指针:智能指针可以定制删除器,可以解决互斥锁问题(使用引用计数,或根本就阻止了复制),防范dll问题(用户在一个dll中new创建,却在另一个dll中delete,使用智能指针会智能追踪到让引用归零的delete)

class Date {
	Date(int year,int mounth,int days)
	{}
};//无任何限制,用户可能输入任何值作为年月日,只能构造函数函数内进行检测

----------------------------------------------------------------------------

struct Year {
	Year(int year);
};
struct Mounth {
	Mounth(int mounth);
};
struct Days {
	Days(int days);
};
class Date {
	Date(const Year& y, const Mounth &m, const Days &d);
};//使用类型限制,用户需要使用相应值定义固定的类型

----------------------------------------------------------------------------

struct Year {
	Year(int year);
};
struct Mounth {
public:
	static Mounth January() { return Mounth(1); }
	//以此类推,提供全部的月份

private:
	Mounth(int mounth);
};
struct Days {
	Days(int days);
};
//private

class Date {
	Date(const Year& y, const Mounth &m, const Days &d);
};//不仅使用类型限制,并且限制相应类型可能的值,用户只能使用已有的月份

条款十九

在定义一个type之前考虑周全:

1、新定义的类型如何销毁创建

(考虑析构与构造,explicit,operator new与delete等问题)

2、对象的初始化与赋值有什么区别

(拷贝构造函数与operator=等)

3、新的type在pass by value(值传递)时应当怎么做

(比如拷贝构造delete,像iostream一样)

4、什么是新type的合法值

(比如提供static的获取值函数,或是对成员值合法的检测)

5、新的type是否需要配合某个继承体系

(类型是否受基类的限制,哪些函数为virtual,尤其是析构函数)

6、新的type需要哪些类型转换

(显示隐式转换,单一构造函数是否explicit)

7、需要的操作符与函数

(成员函数和操作符的定义)

8、什么样的标准函数应当被驳回

(不会被类外使用的函数定义成private)

9、该使用那些成员

(哪些成员为public,什么定义为友元,是否使用嵌套类)

10、什么是这个类型的“未声明接口”

11、新的类型有多么一般化

(决定你要不要把它定义成泛型,或者要不干脆用泛型函数代替,而不是声明新类)

12、你是否真的需要一个新的类型

(如果只是为了拓展基类的功能,或许使用更多的成员函数,或泛型函数会更好)

条款二十

以pass-by-reference-to-const代替pass-by-value

以常量引用代替值传递

可以减少参数的复制消耗,还有函数结束时析构函数的消耗

对于内置值类型和STL的迭代器与函数对象(并不是容器本身),可以选用pass-by-value时还是使用值传递。

条款二十一

并不是所有时候都返回引用更好

不要返回局部变量的指针、引用(大部分时候你返回local stack会得到编译器的警告);不要返回一个动态对象(heap-allocated需要你手动删除,但是鬼晓得你会不会delete);也不要在可能需要多个对象的时候返回一个local-static的pointer/reference

class num {
	friend const num& operator*(const num&, const num&);
};

const num& operator*(const num& lhs, const num& rhs)
{
	static num result;
	//计算
	return result;
}
//很显然,当你比较两个乘式的结果时出现问题
//(a * b) == (b * c)永远为true,因为返回的是static对象的引用,第二次计算后改变static
//对象的值,两个结果都绑定这个对象,故永远相等。

//很显然,当你比较两个乘式的结果时出现问题

//(a * b) == (b * c)永远为true,因为返回的是static对象的引用,第二次计算后改变static

//对象的值,两个结果都绑定这个对象,故永远相等。

猜你喜欢

转载自blog.csdn.net/qq_37051430/article/details/83066580