首页 > 编程语言 >阅读《Effective c++》第三版 day 3

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

时间:2023-12-06 23:58:03浏览次数:50  
标签:std ... Widget Effective c++ class swap myclass day

·考虑提供更搞效且安全的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(...));
}

标签:std,...,Widget,Effective,c++,class,swap,myclass,day
From: https://www.cnblogs.com/XuefengMing/p/17880801.html

相关文章

  • day11 Jenkins Pipeline语法-Jenkins基于Gitlab的授权认证 (4.3.1-4.4)
    一、JenkinsPipeline语法上JenkinsPipeline语法Jenkins有多种方式实现交付流水线。其中,JenkinsPipeline是一种比较流行的方式,它提供一个DSL(DomainSpecificLanguage的缩写,)来描述交付流水线。官网地址:https://www.jenkins.io/doc/book/pipeline/syntax/1、什么是Jenkin......
  • C++中的函数重载(C++Primer)
    一、什么是函数的重载在同一作用域下,几个函数的函数名相同,但其内部的形参列表不同,我们称之为函数的重载,这里的不同不仅仅指形参数量的不同,还包括形参类型的不同。voidprint(constchar*const_p);voidprint(constchar*begin,constchar*end);voidprint(constint*const_p)......
  • C++实现LL1语法分析器
    C++实现LL1语法分析器:预备知识:​ LL1分析法是一种确定的自上而下的分析方法,通过在输入中向前看固定个数(通常为1)的符号来选择正确的产生式从而实现预测分析的效果,预测分析不需要回溯。​由以上定义,LL1分析器是一种表驱动的语法分析器,分析器依赖于语法分析表,需要在输入......
  • Day21 顺序结构及选择结构中的If结构
    顺序结构Java的基本结构就是顺序结构,从上到下的顺序执行,是任何一种算法都离不开的基本算法结构packagecom.baixiaofan.struct;publicclassShunXuDemo{publicstaticvoidmain(String[]args){System.out.println("hello1");//按顺序一句一句执行......
  • C++_调用函数以及不同数据类型
    调用其他文件中的函数add_library可以生成我们所需的动态库或者静态库,我们要把制作出的库文件发布出去供第三方使用一些函数方法单独在一个cpp的源文件中定义,然后在另一个源文件中需要用到自定义的函数时直接调用就可以了!方法1.学过c++的人可能最熟悉的方法还是利用头文件......
  • C++中的const用法
    一、修饰普通变量:constdoublePI=3.14159;constinta=10;//这些C++中的内置类型被const修饰时,其值不可被随意更改二、修饰一个指针(即指针对应的值不可修改):但是指针作为一个变量,其可指向其他位置,只是无法通过指针来改变其指向的对象的值inta=10;intb=20;constint*p......
  • Leetcode刷题day7-字符串.反转ⅠⅡ.反转单词.右旋转
    344.反转字符串344.反转字符串-力扣(LeetCode)编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用O(1)的额外空间解决这一问题。示例1:输入:s=["h","e","l","l","o"]输......
  • java与算法Day1 Scanner的应用(一)
    java中使用输入需要用到java.util.Scanner。Scanner有next,nextInt,nextString,hasNext,hasNextLine等方法。使用XXX variable=Scanner.NextXXX就可以获取一个输入值。next系列的方法,他们的作用都是从键盘中接收数据。当程序执行到他们的时候,在命令行中就开始等待键盘输入了,而......
  • day 01 计算机基础和环境搭建
    1.计算机基础1.1基本概念计算机的组成计算机是由多个硬件组合而成,常见的硬件有:CPU、硬盘、内存、网卡、显示器、机箱、电源...操作系统用于协调计算机的各个硬件,让硬件之间协作工作,以完成某个目标-Windows-Linux-Mac软件在安装上操作系统之后,我们会在自己电......
  • Qt/C++视频监控拉流显示/各种rtsp/rtmp/http视频流/摄像头采集/视频监控回放/录像存储
    一、前言本视频播放组件陆陆续续写了6年多,一直在持续更新迭代,视频监控行业客户端软件开发首要需求就是拉流显示,比如给定一个rtsp视频流地址,你需要在软件上显示实时画面,其次就是录像保存,再次就是一些周边的处理比如贴OSD,做图片分析等。拉流显示是第一步,如果有跨平台的需求,个人推荐......