设计不能被拷贝的类

c++11是下边的用法,c++98就是将拷贝构造变成私有,并且只声明并不实现

1
2
3
4
5
6
7
8
9
10
class A
{
public:
A(int val):_val(val){}
A(const A& a) = delete;
A& operator=(const A& a) = delete;
private:
int _val;
};

设计只能在堆上创建的类

方案1、将析构函数私有化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class HeapOnly
{
public:
void destroy()
{
delete this;
}
private:
~HeapOnly(){}
};
int main()
{
HeapOnly* hp = new HeapOnly;
hp->destroy();
return 0;
}

方案2、构造函数私有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class HeapOnly
{
public:
static HeapOnly* CreatObj()
{
return new HeapOnly;
}
// 防止拷贝
HeapOnly(const HeapOnly& hp) = delete;
private:
HeapOnly(){}
};
int main()
{
HeapOnly* hp = HeapOnly::CreatObj();
delete hp;
return 0;
}
注意:也要把拷贝构造给删除掉

设计只能在栈上创建的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class StackOnly
{
public:
static StackOnly CreatObj()
{
return StackOnly();
}
private:
StackOnly() {}
};
int main()
{
StackOnly sk = StackOnly::CreatObj();
// 避免不了下边的情况
static StackOnly copy(sk);
StackOnly* copy2 = new StackOnly(sk);
return 0;
}

解决new

1
2
3
4
5
6
7
8
9
10
11
class StackOnly
{
public:
static StackOnly CreatObj()
{
return StackOnly();
}
void* operator new(size_t n) = delete;
private:
StackOnly() {}
};

但是静态区的拷贝构造还是不能被禁止。

设计不能被继承的类

1
2
3
4
5
6
7
8
9
10
11
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承  
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit(){}
};

在C++11中有了final关键字,修饰类就说明该类不能被继承。

1
2
3
4
class A final
{
// ....
};

设计只能创建一个对象的类(单例)

饿汉模式

构造函数私有,在静态区创建一个对象,

  • 简单,没有线程安全问题
  • 一个程序中,多个单例,并且有先后创建初始化的顺序要求时,饿汉无法控制,比如多个文件,就无法控制顺序
  • 饿汉的单例类,初始化时任务多,会影响程序的启动速度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class A
{
public:
static A* getInstance()
{
return _a;
}
void fun()
{
std::cout << "调用fun()\n";
}
private:
A(){}
static A* _a;
};

A* A::_a = new A;


int main()
{
A* p = A::getInstance();
p->fun();
}

懒汉模式

第一次使用对象再创建实例对象

  • 可以控制创建的顺序
  • 不影响启动速度
  • 相对复杂,有线程安全问题
1
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
34
35
36
37
38
39
40
class A
{
public:
static A* getInstance()
{
if(_a == nullptr)
{
_a = new A;
}
return _a;
}
void fun()
{
std::cout << "调用fun()\n";
}

// 实现一个内嵌垃圾回收类
class CGarbo {
public:
~CGarbo()
{
if (_a)
delete _a;
}
};

private:
A(){}
static A* _a;
};

A* A::_a = nullptr;
// 回收对象,main函数结束后,他会调用析构函数,就会释放单例对象
static A::CGarbo gc;

int main()
{
A* p = A::getInstance();
p->fun();
}

单例对象释放

  • 一般情况下,单例对象不需要释放,因为一般整个程序运行期间都可能用它
  • 单例对象在进程正常结束后,也会资源释放
  • 有些特殊场景需要释放,比如单例对象析构时,要进行一些持久化操作(往文件数据库写)操作
  • 释放时,可以做个内部类,如上边代码