C++_Effective_C++, 优化程序设计的55个做法

本文详细介绍了C++编程中的最佳实践,包括何时使用私有继承、如何通过组合实现“has-a”或“is-implemented-in-terms-of”、避免重定义继承函数的默认参数值、避免非虚拟函数的重定义等。强调了正确使用继承、组合、常量、初始化、资源管理以及避免不必要的类型转换和减少编译依赖的重要性。
摘要由CSDN通过智能技术生成

39. use private inheritance judiciously

回想 (public时, Student : public Person, 即, 学生 是 一种 人) 这个例子;
而且, 编译器, 在必要时, 会自动的, 将Student 强转为 Person;

但是, 如果是: private继承, 编译器不会自动强转, 而且我们思考下
Person有一个public void eat(), 而如果是B : private继承 Person, 这个B里, 是没有eat
B不具备: eat功能, 也说明: B 不是 Person人;


B : private继承 A 纯粹是一种 (实现技术), 他在 (观念 或 逻辑意义上) 没有实际意义
仅仅表示: B 根据A 实现出来, 即implement in terms of...(根据…实现)


而我们知道, (38 条款中), 复合也是有implement in tems of意义, 两者如何选择呢?

优先使用 (复合), 必要时才使用(private继承)
何为(必要时)呢? 当涉及到(virtual虚函数时, 或 protected数据时), 使用private继承


class Timer{
   
public:
	virtual void time_out() = 0;
}

比如, 我们有一个Widget类, 需要使用Timer计时器功能, (只需重写time_out()即可)

private继承

class Widget : private Timer{
   
private:
	virtual void time_out(){
    ...}
}

正确, 但这样设计不好, 最好的设计是 (复合)

复合

class Widget{
   
private:
	class MyTimer : public TImer{
   
	public:
		virtual void time_out(){
    ...}
	};
	MyTimer timer;
}

这种方式 (public继承 + 复合), 是更好的

38. model “has-a” or “is-implemented-in-terms-of” through composition

in-terms-of: 通过 通过 (复合) 来塑造 (“是一种” 或 “由…组成”)

所谓 composition: 复合, 很简单, 就是指 (成员变量); 只是, 这个 (成员变量), 所表现出的 (含义): 有两种

has-a

class Address{
   };

class Person{
   
	Address addr;
}

Person中有一个addr, 这addr, 就称为: Person中, (复合)了一个 addr

这里的(复合), 塑造的是一种 (has-a): 即Person 有一个 addr


is-implemented

template <class _T>
class My_Set{
   
public:
	void insert( _T _t);
	bool contains( _T _t);
	void remove( _T _t);
private:
	::std::vector< _T> vec;
}

比如说, 我们需要一个set集合, (但是, 又不想使用std::set, 因为: std::set为了追求极度的时间, 而带来的多余的空间消耗)

我们先自己写一个set, 他虽然是O(n)的时间, 但是, 他的空间 要比std::set好;

此时, 这个 (成员变量vec), 他是 通过(复合)的方式, 表现出的: 不再是"has-a", 而是: “is-implemented-in-terms-of”

即(通过xxx来实现), 即: 通过 (成员变量std::vector), 来实现, 我们的My_Set这个类

37. never redefine a function’s inherited default parameter value

当一个类Son, 从他的父类Fa, 已经继承了一个名为func的函数, 然后自己Son又定义了一个func函数
此时一定有: (func为虚函数 否则, 违背36条例)

而, Fa::func( int a = 123);, 假如这个虚函数, 是有 (缺省参数)的!!
此时, 如果Son::func( int a); 或者 func( int a = 666);, 这就会引起歧义;

(虚函数的作用是: 动态绑定)
而, ( 函数的"缺省参数", 是: 静态绑定)

最好的处理是: (虚函数里, 不要有 “缺省参数”)

36. never redefine an inherited non-virtual function

子类中, 不要定义一个, 从父类继承而来的 非虚函数 的函数
即, 在继承体系里, 非虚函数, 不可以有 (相同的名称);
比如, 父类有一个func的非虚函数, 子类不可以再定义func这个名称

注意, 以下讨论时, 仅涉及函数名, 无关(返回值和参数); 因为我们这里探讨的是: (函数名遮掩) 的问题


举个例子:

class Fa{
   
public:
	void f0();
};

class Son : public Fa{
   
public: 
	int f0( int);
}

Son s;
Son * p0 = &s;
Fa * p1 = &s;

以上代码虽然没有错, 但是, (由于, f0函数不是虚函数, 即, 他会是: static bound静态绑定)
如果是虚函数, 他是动态绑定, 即p0和p1 的f0函数, 会是同一个
但是此时, 由于不是虚函数, p0的f0函数 是Son的, p1的f0函数 是Fa的

而, 根据 32条款, public继承, 呈现的是: is-a的关系

  • 如果子类要重新父类函数, 则使用 (虚函数)
  • 那么, 对于(非虚函数), 即, 说明, 不存在 (重写)的关系; 当然, 此时有2种情况;
    • 未继承下来; (比如, 父类有一个func函数, 但是, 子类的继承权限不够, 没有继承下来 父类的func)
      那么, 子类定义 一个名为func的函数, 这没问题; 不存在 函数遮掩的问题
    • 继承下来了;
      0, (因为, 这个父类的func函数, 他不是virtual, 说明该函数不支持重写)
      1, (又因为, 这个函数 继承下来了; 所谓继承, 不就是想调用 父类的函数嘛)
      由0,1 分析, 子类要使用这个func函数, 就应该是: 父类的func函数, 子类不可以再定义!!!

即, 对于一个(非virtual)的 函数func, 父类有一个func该函数一定是实现了的,
如果子类 的继承权限, 把父类func 给继承下来了, 则, 子类, 绝对不可以写一个 (重名)的 func函数; {否则造成严重的歧义}

32. make sure public inheritance models is-a

当使用public继承时, 就要想到: Student : public Person这个例子, 即 学生, 拥有 "人"的 所有特性
编译器会在必要时, 会自动的, 将(学生) 强转为 (人); 0


确保你的 (publicj继承), 塑造出的 是一种 (is-a)的 关系

当出现: class A : public B{}, 此时 (不管有没有virtual函数), 就意味着: A is a B
即, AB构成: is a关系

什么是is a关系呢? 即(是一种)

比如: student is a person


因为, 但以public继承时, 子类里的那个父类对象, 可以使用所有的public函数, 这和 单独一个父类对象的访问权限 完全相同;

所以, 你需要满足: 在逻辑上, A is a B 的前提下, 才可以去使用: A : public B的继承;
如果逻辑上行不通, 则不要public继承

因为我们知道, 任何子类 是完全可以强转为 父类的 (因为, 子类里, 就有一个父类对象)

35. consider alternatives to virtual functions

假设有很多的(人物Character), 每个人物, 都有一个 (获取血量)的函数;


方式0: virtual

class Character_Base{
   
public: 
	virtual int get_health() = 0;
};

class Character_Hero{
   
public: 
	virtual int get_health(); // override
};

这是很自然的设计方案


方式1: NVI: non-virtual interface, NVI思想 称: 所有的接口即对外的public, 都不是 virtual.
(即所有virtual函数, 都是private)

class Character_Base{
   
public;
	int get_health(){
   
		// 0
		int ret = get_health_do();
		// 1
		return ret;
	}
private: // 注意这里是private
	virtual int get_health_do() = 0;
};

class Character_Hero{
   
private: // 注意这里是private
	virtual int get_health_do(); // override
};

和(方式0) 基本差不多, 只是: (将virtual函数 均改为private), (在父类, 统一设置public对外函数)

  • 所有的子类, 都是使用的 父类::get_health()的这个public函数
    子类, 只需要 实现 (private的虚函数)

这个public父类::get_health(), 统一的 对外函数, 这个函数 也称为 虚函数的 wrapper;

这样设计的好处是: (又封装了一

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值