effective c++ item35-39

item35:考虑虚函数的替代方案

NVI----Non-Virtual Interface

不使用虚函数接口,子类也可以实现按需求更改实现:
在这里插入图片描述

The Strategy Pattern via Function Pointers

也可以使用函数指针
在这里插入图片描述
或者使用stl::function
在这里插入图片描述

The “Classic” Strategy Pattern

item36:不要重新定义non-virtual函数

见https://blog.csdn.net/weixin_44609676/article/details/130382495

item37:不要重新定义重写函数的默认参数

在这里插入图片描述
可以看到,b和pb调用的是同一个虚方法,但是输出到结果却不一样。因为f是虚函数,在运行时才动态绑定需要执行的函数,但是参数却是静态绑定的,在编译期pb的参数就已经是1了。
即使把BC的默认参数都换成1,也不推荐这样做。因为会增大维护代码的成本,如果默认参数要改,全部的相关代码都要改
可以使用第35条款的NVI来解决:

class A{
private:
	virtual void f(int x) const = 0;		// 子类复写f方法,调用dof方法
public:
	void int dof(int x = 1) const{			// 非虚,不允许复写
		cout << "A" << x <<endl;
	}
}

item38:通过组合建模出has-a或实现关系

has-a关系
  • has-a:For example, consider a “Car” class that has a “Engine” class as a member variable. The Car class has-a Engine, which means that the Engine object is a part of the Car object, and it cannot exist without the Car. Similarly, the Car class may have other has-a relationships with classes such as “Wheels”, “Transmission”, “Seats”, etc.
  • is-a:For example, consider a “Vehicle” class and a “Car” class. The Car class is a specialized version of the Vehicle class, meaning that it inherits all of the attributes and behaviors of the Vehicle class, but it also has its own unique attributes and behaviors that make it different from other types of vehicles.
实现关系

在这里插入图片描述
使用list来实现set,如果用public继承,就会违反is-a的关系,因为set并不是list。就可以用包含关系来实现。

item39:EBO(空基类优化)

empty base optimization

任何对象大小至少为1(c++20:使用[[no_unique_address]])
然而,基类子对象不受此约束,而且完全可以从对象布局中优化掉
在这里插入图片描述
B的大小为8,因为内存对齐,在A a后面补上了3个字节。
C这种现象就叫EBO。
空类不一定是真的为空,也可以是包含了typedefs、enums、static、non-virtual函数等

不应用EBO的场景

如果某个非静态数据成员的类型与一个空基类的类型相同或者由该空基类派生,那么禁用空基类优化。因为要求两个同类型的基类子对象在最终派生类型的对象表示中必须拥有不同地址。
在这里插入图片描述
派生类B的内存布局是:A占一个字节,成员变量a占一个字节,然后补2个字节,int占4个字节
派生类C的内存布局是:A不占内存,int占四个字节
派生类D的内存布局是:A占一个字节,空3个字节,c、int都占4个字节
在MSVC中,有且仅有最后一个空基类应用空基类优化,其余空基类均不应用空基类优化,并分配一个字节。在GCC中,无论存在多少个空基类,均应用空基类优化,不分配任何空间,空基类地址与派生类首对象地址相同。

no_unique_address注解(C++20)

如果空成员子对象使用属性[[no_unique_address]] ,那么允许像空基类一样优化掉它们。取这种成员的地址会产生可能等于同一个对象的某个其他成员的地址。
如下面的例子,b的地址和b内成员变量的地址相同。
在这里插入图片描述
在这里插入图片描述

item39:明智的使用private继承

class A{};
class B:private A{};
void func(const A& a);
....
A a;
B b;
func(a);		// right
func(b);		// error

private只是实现上的继承,不包括接口上的继承,不是is-a的关系。A和B已经没有关系了,只是对软件实现上有意义。
当我们需要访问基类中的protected成员,或者是要复写虚函数时,可以考虑私有继承。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值