EffectiveC++-----3-4章(条款13-25)

条款13
以对象管理资源

//使用auto_ptr或者shared_ptr管理资源
class Investment{...}
...
Investment* createInvestment();
...
void f()
{
	Investment* pInv = createInvestment();
	...
	delete pInv;	//容易忘记delet出现内存泄露
}
...
//以下是改进
void f()
{
	auto_ptr<Investment> pInv(createInvestment());
	...
	//在指针被销毁时同时销毁内存占用
}
...
auto_ptr<Investment> pInv1(createInvestment);
auto_ptr<Investment> pInv2 = pInv1;
//以上情况下将 pInv1赋值给pInv2之后,pInv1被设置成NULL,内存交给pInv2管理,即删除时机决定于pInv2
//改进方法如下
void f()
{
	shared_ptr<Investment> pInv1(createInvestment());
	shared_ptr<Investment> pInv2 = pInv1;
	...
	//以上写法只有在pInv1和pInv2都被销毁时才销毁内存数据
	//采用引用计数型智慧指针
}

条款14
在资源管理类中小心coping行为

//涉及多线程,此处不讨论

条款15
在资源管理类中提供对原始资源的访问

shared_ptr<Investment> pInv(createInvestment());	//此时pInv是shared_ptr<Investment>类型变量
Investment* p = pInv.get();	将pInv转化成Investment*类型的方法---调用get方法
//shared_ptr<>重载了->和*运算符来直接访问原始资源数据
...
class Font
{
public:
	explicit Font(FontHandle fh):f(fh){}
	~Font(){...}
private:
	FontHandle f;
}
//以上类若在使用时要访问FontHandle必须添加一个get类
...
FontHandle get()const{return f;}
//但是每次都调用get是不方便的
//解决方法
class Font{
public:
	...
	operator FontHandle()const{return f;}
	...
}
//以上方法在将Font赋值给FontHandle类型变量时会自动转换类型成FontHandle类型
...

条款16
成对使用new和delete时要采用相同形式

string * stringArray = new string[100];
delete stringArray	//只删除了第一个元素
...
delete[] stringArray;	//删除所有元素
//先读取一长串的内存空间解析成string类型,不管是不是属于string[100]之内,挨个调用构造函数进行销毁
...

条款17
以独立语句将new过的对象置入智能指针

int priority();
void processWidget(shared_ptr<Widget> pw, int priority);
...
processWidget(shared_ptr<Widget>(new Widget), priority());
//以上语句做了三件事
//执行new Widget
//调用priority
//调用shared_ptr
//以上三者的顺序未定,如果在调用priority的过程中抛出异常,会导致new Widget返回的指针丢失,因为还未被放入shared_ptr内,导致内存泄漏
//解决方法---分两行写
shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority());

条款18
让接口容易被正确使用,不易被误用

//关键点---shared_ptr允许当智能指针被建立起来时指定一个删除器deleter
shared_ptr<Investment> pInv(static_cast<Investment*>(0), getRidOfInvestment);
//建立一个null的shared_ptr并自定义删除其getRidOfInvestment
...
//shared_ptr会自动使用它的每个指针专属的删除其进行删除
//消除了cross-DLL-problem的隐患
//即在一个DLL中创建的变量在另一个DLL中delete,此时由于处在不同的堆,"delete"是不一样的,所以会导致错误
//而shared_ptr会自动跟踪到指针创建的那个DLL的堆里的delete进行删除

条款19
设计class犹如设计type

//一下几点注意事项
//1.新type的对象应该如何被创建和销毁
//2.对象的初始化和对象的赋值有什么样的差别
//3.新type的对象如果被pass-by-value意味着什么
//4.什么是新type的合法值
//5.你的新type需要配合某个继承图系吗
//6.你的新type需要什么样的转换
//7.什么样的操作符和函数堆此新type而言是合理的
//8.什么样的标准函数该驳回(哪些是private)
//9.谁该取用新type成员(哪些变量是public哪些是private)
//10.什么事新type的未声明接口
//11.你的新type有多一般化

条款20
宁以pass-by-reference-to-const替换pass-by-value

class Person{
...
private:
	string schoolName;
	string schoolAddress;
...
}
class Student:public Person{}
...
bool validateStudent(Student s);
Student plato;
...
validateStudent(plato);
//此步执行的操作有
//调用形参s的父类Person的复制构造函数
//调用形参s的类Student的复制构造函数
//调用string的复制构造函数
//优化方案
bool validateStudent(const Student& s);	//const为了让函数内部无法改变参数的值
//此方案未调用任何构造函数,仅仅将plato取了别名
//传引用的方法同时解决了对象切割问题:如以下
class Window{
...
	virtual void display()const;
}
class Something:public Window{
	virtual void display()const;
}
...
void print(Window w);
...
Something s;
print(s);
//上述例子中将s传递给w之后形参w只会执行Window类的复制构造函数将s的父类成分复制
//解决方法
void print(const Window& w);

条款21
必须返回对象时,别妄想返回其reference

const Rational& operator* (const Rational& lhs, const Rational& rhs)
{
	...
	Rational reasult(...);
	return reasult;
}
//由于reasult是局部变量,在return后被销毁,返回的引用指向了一个不存在的对象
const Rational& operator*(同上)
{
	static Rational reasult;
	...
	return reasult;
}
//以上写法在遇到如下情况时出错
if((a*b) == (c*d)){...}
//可以发现if内部的表达式始终为true因为static变量的空间只有一个,始终进行判断的都是当前static变量内容

条款22
将成员变量声明为private

不解释

条款23
宁以non-member,non-friend替换member函数

class Web
{
public:
	...
	void doSomething();
	...
}
...
void doSomething(Web& b){...}
//通常用以上方法代替前面的member方法
//若要访问Web中的private变量只需在类中声明doSomething为friend
//通常将Web与doSomething封装在一个namespace中
namespace WebStuff
{
class Web{...}
void doSomething(Web& b){...}
}

条款24
若所有参数皆需类型转换,请为此采用non-member函数

class Rationel{
...
public:
const Rational operator*(const Rationel& rhs)const;
...
};
Rational a,b;
a = b*2;	//成功
a = 2*b;	//失败 因为2不是类,没有重载的运算符,无法接收Rational类型的变量
//解决方法 写成non-member函数
const Rational operator*(const Rational& lhs,const Rational& rhs)
{
	...
}
//以上函数通常放在类的public里而不是friend,原因是为了保证封装性

条款25
考虑写一个不抛出异常的swap函数

namespace std{
	template<typename T>
	void swap(T& a,T& b)
	{
		T temp(a);
		a = b;
		b = temp;
	}
}
...
class Widget
{
...
private:
	Widget* p;
}
Widget a(...);
Widget b(...);
swap<Widget>(a,b);
//若采取以上方法,会复制大量没用的数据,但是需要的仅仅是private里的p
//解决方法
namespace std
{
	template<>	//特例化
	void swap<Widget>(Widget& a,Widget& b)
	{
		swap(a.p,b.p);	//仅交换p指针
	}
}
//由于p是private,所以编译不通过
//解决方法
class Widget
{
public:
	...
	void swap(Widget& other)
	{
		using std::swap;
		swap(p,other.p);
	}
	...
}
namespace std
{
	template<>
	void swap<Widget>(Widget& a,Widget& b)
	{
		a.swap(b);
	}
}
...
...
namespace std
{
	template<typename T>
	void swap<Widget<T>>(Widget& a,Widget& b)
	{
		a.swap(b);
	}
}
//以上写法是错误的
...
template<typename T>
void doSomething(T&a,T& b)
{
	using std::swap;	//若写成std::swap(a,b)则调用的是std里的swap而不是挑选最适合的swap
	//类似
	//using a;
	//using b;
	//这样调用几个命名空间,调用的时候就自动选择最合适的
	...
	swap(a,b);
	...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值