阅读《Effective c++》第三版 day 3

发布时间 2023-12-06 23:46:18作者: 雨和风

·考虑提供更搞效且安全的swap函数:

对于一般缺省的swap函数,可能引发拷贝构造函数,导致性能下降,swap应设计为提供异常安全性保障而不抛出异常,但缺省版本的swap时可能抛出的,所以重新设计swap是应该的。此前设计 operator= 函数也有稍微提过。此外考虑类的设计模式,也会有低效率的swap函数,如pimpl:

在 Widget.h 头文件下:
class WidgetImpl{};	//前向声明,用于做Widget类的成员指针
class Widget{
	Widget();
	~Widget();
	void interface()	//公共接口
private:
	WidgetImpl *ptr_WidgetImpl
};
在 Widget.cpp 实现文件下:
class WidgetImpl{
	...//Widget类功能
priavte:
	...//widget类的资源
};

假设需要为Widget准备一个swap函数,直接调用缺省swap函数相当于:

void swap(Widget &lhs,Wdiget &rhs){
	Widget temp(lhs);	//引发了拷贝构造,拉低效率
	lhs = rhs;		//拷贝构造函数是运行抛出异常的
	rhs = tmp;
}

比较优化的实现方式是:

//1: 提供public swap member成员函数,让其高校的置换两个对象值:
class Widget{
public:
	void swap(Widget &other){
		using std::swap;
		swap(pImp,other.pImpl);	//一一对资源交换
	}
...
};

namespace std{
	template<>		//对std::swap版本的进行特化
	void swap<Widget>(Widget& a,Widget& b){
		a.swap(b);	//调用成员swap
	}
}

//2: 在Widget 或 template 所在的命名空间提供一个non-member //swap,并令其调用上述swap成员函数:
namspace Widgetstuff{
	...			//模板化WidgetImpl
	template<typename T>
	class Widget{...};	//模板Widget包含swap成员函数
	template<typename T>
	void swap(Widget<T>&a,	//有时设计类时non-member 版本
		  Widget<T>&b){	//比menber 版本更要好,所以
		a.swap(b)	//比较推荐不容易使类设计变大的
	}			//做法
};

//3: 正在编写一个非模板类时,为std::swap以你的类进行特化,并令其调//用你的swap函数,点1示例。

//[注意]:在编写函数模板时,应该在内部调用前 using std::swap,保
//证swap在有无特化版本下都能够选对模板。
template<typename T>
void fun(T& a,T& b){
	using std::swap;
	sawp(a,b);
}

·将文件间的编译依存关系降至最低

一个成熟的程序其内部的类结构是极其复杂的,互相包含引用,错综复杂。对于过于耦合的类设计,文件间的编译依存关系是很严重的,为此我们通常要进行解耦,降低文件间的编译依存关系。一个庞大的类,类部肯定包含相当多的其他类类型,每当对内部的类结构做更改时,其内存结构也会做很多更改。特别是,当我们声明一个类变量的时候,编译器会根据类结构开辟内存空间,并且涉及构造函数的调用。内存资源很快就会被占用上去。为此,我们有两种设计类的方式:Handle class 和 Interface class 两种方式,来延迟编译器对类结构的分析,减少一开始对类文件的依赖。这两种方法都基于运用指针。
Handle class: 实质是pImpl,用类管理类。

在myclass头文件中:

   #ifndef MYCLASS_H 
#define MYCLASS_H
//#include "xxx1.h"	//原myclass所需要的资源可以不需要了
//#include "xxx2.h"	//原myclass所需要的资源
class xxx1;
class xxx2;		//用类声明替换
//将原本的其他成员变量被一个realmyclass类或结构体包裹代替
class realmyclass;	//以类类型为例
class myclass{
public:
	myclass(...);	//构造函数,所有成员函数不做实现
	~myclass(...);	//只做声明
	func1();
	func2();
	....
private:
	std::shard_ptr<realmyclass>ptr;//推荐使用智能指针管理
};
#endif

在myclass类实现文件中:

#include "myclass.h"
#include xxx1		//提供资源头文件
#include xxx2
class realmyclass{
private:
	xxx1 parm1;	//原来在myclass的成员资源移动到此
	xxx2 parm2;
	...
public:
	realmyclass(...)	//提供相同的构造函数构造函数
	func1(){...};
	func2(){...};		//于myclass同名的成员函数
	...
};
//myclass的构造函数调用myclass的实现类指针
myclass::myclass(...){
	this->ptr = std::shared_ptr<realmyclass>(...)
}
//myclass 的成员函数靠指针调用实现类中的函数
myclass::func1(){this->ptr->func1;}
myclass::func2(){this->ptr->func2;}
//完成类的实现,视情况添加,此处shared_ptr可不做要求
myclass::~myclass() = default;

Interface Handle:通过继承的方式,子类做实现,让父类成为接口提供子类方法的调用。有点像工厂设计模式。

myclass类头文件:

#ifndef MYCLASS_H
#define MYCLASS_H
class xxx1;
class xxx2;
class myclass{
public:
	virtual ~myclass() = default;
	virtual func1() const = 0;	//声明为纯虚函数
	virtual func2() const = 0;
	//通过返回一个静态对象,同样先不做实现
	static std::shard_ptr<myclass> create(...);
};
//有需要的可以将构造函数添加delete关键字或者声明为private,防止不期望下调用构造函数获取对象。
#endif

derived_myclass类头文件:

#ifndef DERIVED_MYCLASS_H
#define DERIVED_MYCLASS_H
#include "myclass.h"		//包含父类头文件
#include xxx1;			//包含资源头文件
#include xxx2;
class derived_myclass : public myclass{
public:
	derived_myclass(...):... //构造函数,通过列表参数初始化
	func1() const override{...}//重写虚函数,实现方法
	func2() const override{...}
private:
	xxx1 parm1;	//原先myclass的成员资源移动过到此
	xxx2 parm2;
};
#endif

derived_myclass类实现文件:

//不要忘了实现父类中的静态类成员方法,记住父类指针可以接收子类对象
static std::shard_ptr<myclass> myclass::create(...){
	return std::shared<myclass>(
			new derived_myclass(...));
}