本章共有8个条款,这次学习如下前6个:
在设计程序的过程中,会有 function 接口、class 接口、template 接口等各种各样的接口。
要保证接口易用、不被误用,首先要考虑客户可能犯什么样的错误。
设计类时需要注意的问题:
class A
{
public:
A(){
cout<<"constructor"<
运行结果如下:
constructor
***
destructor
destructor
首先, A 的拷贝构造函数会被调用,以 b
为蓝本将 a
初始化,然后函数结束后 a
会被销毁,会调用一次析构函数。所以参数传递的成本是“一次 A 拷贝构造函数调用,加上一次 A 析构函数调用”。
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 更恰当。
函数创新建对象的途径有二:在 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);
}
所以要谨记:
设计类时一定要考虑封装性!!!
看下面例子
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