技术(techniques, idioms, patterns)
#
# 技术(techniques, idioms, patterns)
这部分内容更像是设计模式要解决的相关的问题;
# Item 25:将 constructor和 non-member functions虚化
virtual copy constructor 感觉和设计模式里面的工厂模式是差不多的概念
non-member function的虚化:写一个虚函数做实际工作,再写一个什么都不做的非虚函数,只负责调用虚函数。当然啦,为了避免这种操作受到函数调用所带来的成本,你可以将非虚函数 inline
#include <iostream>
class A
{
public:
virtual std::ostream &print(std::ostream &s) const = 0;
};
class B: public A
{
public:
virtual std::ostream &print(std::ostream &s) const { s << "i am b"; return s; };
};
class C : public A
{
public:
virtual std::ostream &print(std::ostream &s) const { s << "i am c"; return s; };
};
std::ostream &operator <<(std::ostream &s, const A &a) // 这就是那个非虚函数,负责调用上面的虚函数
{
return a.print(s);
}
int main(void)
{
B b;
C c;
std::cout << b << "\n" << c << std::endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Item 26:限制某个 class所能产生的对象数量
限制某个class所能产生的对象数量(如果只是一个, 那么用static对象, 记得看singleton设计模式)
使用引用计数
使用全局函数, 并且用private来封锁ctor和dtor,全局函数里面包裹一个static的对象, 只能产生一个:
class Printer{
public:
void submitJob(const PrintJob& Job);
void reset();
void performSelfTest();
friend Printer& thePrinter();
...
private:
Printer();
Printer(const Printer& rhs);
...
}
Printer& thePrinter(){
static Printer p; // 这里限制了程序只能产生一个P
return p;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
但是全局函数是不安全的, 最好不要动不动就用全局的东西
使用namespace来包裹一下这个全局函数, 会在使用的收, 增加对全局函数作用域的控制;
“class拥有一个 static对象”的意思是:即使从未被用到,它也会被构造(及析构)。相反地“函数拥有一个 static 对象”的意思是:此对象在函数第一次被调用时才产生。如果该函数从未被调用,这个对象也就绝不会诞生(然而你必须付出代价,在函数每次被调用时检查对象是否需要诞生)
# Item 27:要求(或禁止)对象产生于 heap之中
手动限制或者禁止对象产生与heap之中, 把ctor和dtor, operator new(), operator new放在private里面,或者使用=delete
(private不能继承, 但是可以弄成protected的,但是弄成protected就不知道是在heap上还是stack上面了)
class A
{
public:
/* 做一个伪析构 */
void destory() { this->~A(); }
private:
/* 析构函数设为私有,防止在栈上构建对象 */
~A() { };
};
class M
{
// A a_; /* 错误,不可在堆上创建 */
A *pa_; /* 在内含类中,需要改为指针使用 */
};
int main(void)
{
// A a; /* 错误 */
A *pa = new A(); /* 正确 */
// delete pa; /* 错误 */
pa->destory(); /* 正确 */
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
禁止在heap上产生对象, 要把operator new(), operator new以及相应的delete函数放在private里面;
#include <iostream>
class A
{
public:
/* 用c++11的方式 */
/*
static void *operator new(size_t) = delete;
static void operator delete(void *) = delete;
static void *operator new [](size_t) = delete;
static void operator delete [](void *) =delete;
*/
private:
static void *operator new(size_t);
static void operator delete(void *);
static void *operator new [](size_t);
static void operator delete [](void *);
};
int main(void)
{
A a;
// A *pa = new A(); /* 错误 */
// A *pa = new A[10]; /* 错误 */
A *p = &a;
// delete p; /* 错误,delete已经被私有化,编译器就可以报错,delete []同原理 */
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Item 28:Smart Pointers(智能指针)
smart pointer智能指针,在现代C++里面, 主要是auto_ptr(), shared_ptr()相关的类型, 可以使用;
# Item 29:Reference counting(引用计数)
reference counter引用技术 、 copy on write相关的技术
(如何实现引用计数?)
# Item 30:为什么要使用Proxy classes(替身类、代理类)
代理类实际上有三种好处,
- 实现多维数组: 真实数组为一个一维数组,内嵌代理为一个一维数组,组合起来就是一个二维数组
- 区分读写
- 压制隐式转换 因为返回的类型为代理类类型,所以很多操作需要隐式类型转换,如果代理类没实现,就不能进行隐式类型转换,这样可以有效的压制隐式类型转换
# Item 31:如何让函数根据一个以上的对象类型来决定如何虚化
让函数根据一个以上的对象类型来决定如何虚化这个函数;
虚函数的调用其实也可以理解为一个message dispatch, 如果一个虚函数要根据两个参数的类型来进行虚化, 则称为 double dispatch,
使用RTTI, typeid();