Effective C++ 读书笔记(4)

第四章 设计与声明(1)

本章共有8个条款,这次学习如下前6个:


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

在设计程序的过程中,会有 function 接口、class 接口、template 接口等各种各样的接口。
要保证接口易用、不被误用,首先要考虑客户可能犯什么样的错误。

  • "促进正确使用"的办法包括接口的一致性、以及与内存类型的行为兼容
  • "阻止误用"的办法包括建立新类型、限制类型上的操作、束缚对象值、以及消除客户的资源管理责任

条款19 设计 class 犹如设计 type

设计类时需要注意的问题:

  • 新 type 的对象如何被创建和删除?
  • 对象的初始化和对象赋值该有什么样的区别?
  • 新 type 的对象怎么 passed by value ? (copy构造函数)
  • 新 type 的“合法值”?利用约束条件,成员函数(特别是构造函数、赋值操作符)必须进行错误检查工作
  • 新 type 需要配合某个继承图系吗?(析构函数是否为 virtual)
  • 新 type 需要什么样的转换?如果要让类型 T1 被隐式转化为 T2,要在 T1 内写一个类型转换函数(operator T2)或者在 T2 内写一个 non-explicit-one-argument (可被单一实参调用)的构造函数。但是如果你只允许 explicit 构造函数存在,就得写出专门负责执行转换的函数。(条款15有隐式和显式转换的范例)
  • 新 type 需要什么样的操作符和函数?某些是member函数,某些不是,见条款23,24,26
  • 什么样的标准函数应该被驳回?必须声明为 private,比如 copy 函数(条款6)
  • 谁该取用新 type 的成员?根据需求设定成员为 public、protected、private,或者哪一个classes、functions应该是friends
  • 什么是新type的“未声明接口”?他对效率、异常安全性以及资源运用提供何种保证?
  • 新 type 有多么一般化?是否需要定义成 class template?

条款20 宁以 pass-by-reference-to-const 替换 pass-by-value
  • pass-by-value:
class A
{
public:
    A(){
        cout<<"constructor"<<endl;
    }
    ~A(){
        cout<<"destructor"<<endl;
    }
};
void fun(A a){//定义函数,以值传递参数
   
}
int main(){
    A b;
    cout<<"***"<<endl;
    fun(b);
    return 0;
}

运行结果如下:

constructor
***
destructor
destructor

首先, A 的拷贝构造函数会被调用,以 b 为蓝本将 a 初始化,然后函数结束后 a 会被销毁,会调用一次析构函数。所以参数传递的成本是“一次 A 拷贝构造函数调用,加上一次 A 析构函数调用”。

  • pass-by-reference-to-const:
    把函数改成:void fun(const A& a);
    运行结果为:
constructor
***
destructor

说明只有创建和销毁对象 a 时调用了构造函数和析构函数,没有任何新的对象创建,这种传递方式效率会高很多。
另外,by-reference 方式也可以避免“对象切割”问题

class A
{
public:
	virtual void fun();
};
class B:public A
{
public:
	virtual void fun();
};
void fun2(A a)//错误,参数可能会被切割
{
	a.fun();
}

B b;
fun2(b);//此时b会被切割,在函数内调用的总是 A::fun(),而不是 B::fun();

解决切割问题的办法就是以 by-reference-to-const 的方式传递 a

void fun2(A& a);//参数不会被切割,传进去的是什么类型,a 就表现出什么类型

注意:对于内置类型(比如 int)、STL 的迭代器和函数对象,pass-by-value 更恰当。


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

函数创新建对象的途径有二:在 stack 空间或在 heap 空间创建
1)、stack 空间
如果定义一个 local 变量,就是在 stack 空间创建对象。根据这个策略试写 operator*如下:

const A& operator*(const A& a1,const A& a2)
{
	A result(a1.x, a2.y);//糟糕的代码
	return result;
}

这个函数返回一个 reference 指向 result ,但 result 是个 local 对象,而 local 对象在函数退出前被销毁了。
2)、heap 空间
heap-based 对象由 new 创建,下面考虑在 heap 内构造一个对象,并返回 reference 指向它。

const A& operator*(const A& a1,const A& a2)
{
	A *result = new A(a1.x, a2.y);//更糟糕的代码
	return *result;
}

一方面还是要付出一个“构造函数调用”的代价,另外一个问题是:谁该对 new 出来的对象实施 delete ?
正确写法是:就让函数直接返回一个新对象!

inline const A operator*(const A& a1,const A& a2)
{
	return A(a1.x, a2.y);
}

所以要谨记:

  • 绝不要返回 pointer 或 reference 指向一个 local stack 对象(函数退出对象销毁)
  • 绝不要返回 reference 指向一个 heap-allocated 对象(不知道何时delete)
  • 绝不要返回 pointer 或 reference 指向一个 local staic 对象,而有可能同时需要多个这样的对象(因为只保留local static对象的现值)

条款22 将成员变量声明为 private
  • 将成员变量声明为 private,可以保证封装性。
  • 如果对客户隐藏成员变量,可以保证客户访问数据的一致性,可以保证 class 的约束条件得以维护,因为只有成员函数可以影响他们。
  • 如果有一个 public 成员变量被取消,所有使用它的客户码都会被破坏,同样如果取消一个 protected 成员变量被取消,所有 derived class 都会被破坏,因此 protected 也缺乏封装性。

设计类时一定要考虑封装性!!!


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

看下面例子

class A {
public:
	void do_thing1();
	void do_thing2();
	void do_thing3();	
};

为了一次性执行三个函数,可以有如下两种操作

//1、利用member函数

void do_things();//添加 public 成员
				//在 do_things 函数里面调用上面三个成员函数
//2、利用non-member函数
void do_things(A& a)
{
	a.do_thing1();
	a.do_thing2();
	a.do_thing3();
}

封装性比较:member 函数可以看到更多的数据,而 non-member 函数只能看到 member 成员函数,所以 non-member 函数封装性更好。
能访问 private 成员变量的函数只有 class 的 member 函数加上 friend 函数,所以对于提供相同机能的 member 函数和 non-member、non-friend 函数,后者提供更大的封装性,因为它并不增加“能够访问 class 之内 private 成分”的函数数量。

可以将 non-member functions 和 class 放在同一个命名空间内,不同的功能(func()函数)放在不同的头文件中,但是均在一个 namespace 内,客户可以轻松扩展这一组便利函数,这也是C++标准程序库的组织方式,比如 std 内的 vector、algorithm、list 等头文件,不同的功能实现,降低了编译依存性。

//头文件 a.h,针对class A 自身
//以及WebBrowser核心机能
namespace AAA
{
	class A {....};
	...;//核心机能,例如所有客户都需要的non-member函数
}
 
//头文件 b.h
namespace AAA {
	void func1(A& a1);//在 namespace 中根据需要添加扩展函数 func1
}
//头文件 c.h
namespace AAA {
	void func2(A& a2);//在 namespace 中根据需要添加扩展函数 func2
}

参考文章

1、https://blog.csdn.net/vict_wang/article/details/81637048

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔跑的贝塔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值