文章目录
写一个不能被拷贝和复制的类
- 拷贝和赋值重载函数私有化。
- 分C++98和C++11写法;推荐C++11写法。
//C++98
class CopyBan
{
// ...
private:
CopyBan(const CopyBan&);
CopyBan& operator=(const CopyBan&);
//...
};
//C++11
class CopyBan
{
// ...
CopyBan(const CopyBan&)=delete;
CopyBan& operator=(const CopyBan&)=delete;
//...
};
原因
- 设置成私有:如果只声明,没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
- 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
- C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
请设计一个类,只能在堆上创建对象
- 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
- 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
class MyClass
{
public:
static MyClass* get_init()
{
return new MyClass;
}
private:
MyClass()
:_a(0)
{
}
MyClass(const MyClass&)=delete;
MyClass& operator=(const MyClass&) = delete;
int _a;
};
static:
- 静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;
- 而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
- 防止在栈上进行拷贝构造。
请设计一个类,只能在栈上创建对象
- 方法一:同上将构造函数私有化,然后设计静态方法创建对象返回即可。
class MyClass1
{
public:
static MyClass1 get_init()
{
return MyClass1();
}
private:
MyClass1(){}
MyClass1(const MyClass1&) = delete;
MyClass1& operator=(const MyClass1&) = delete;
int _a;
};
- 方法二:屏蔽new:因为new在底层调用
void* operator new(size_t size)
函数,只需将该函数屏蔽掉即可。
class MyClass
{
public:
MyClass() {}
private:
void* operator new(size_t x) = delete;
void operator delete(void* ptr) = delete;
};
原因:
- 有专属的new会调用专属的,向内存池申请,效率更高。
- 没有专属的会调用全局的operator new 向系统申请内存。
- 我写了你就要调用我…,但是你调用到了,我让你用不了
- **虚表:**虚函数表在编译时生成,虚表指针是在 构造函数初始化列表 初始化。
请设计一个类,不能被继承
- C++98
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
C++11:
- final关键字,final修饰类,表示该类不能被继承。
class A final
{
// ....
};
比较:
- C++98可以被继承,但继承的派生类不能创建对象;
- 派生类的对象构造函数必须调用基类的,但基类的私有不可见;所以继承不会报错,创建对象会报错。
设计模式
设计模式:
- 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
- 使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。
- 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
- 单例模式,工厂模式,观察者模式:迭代器模式 、适配器模式等等。
请设计一个类,只能创建一个对象(单例模式)
- 一个类只能创建一个对象,即单例模式。
- 该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
- 比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
- 实现模式分为饿汉模式和懒汉模式。
- 假如定义一个全局对象,在所有文件都是可见的,只能定义在一个头文件里,多个源文件包含时,每个源文件预处理时头文件展开,链接会报错,重定义错误。
- 假如定义一个静态全局对象,只在每个源文件内部可见,都有一份不冲突,但无法保证时同一个。
- 对于C语言,可以在头文件extern声明全局对象,别的原文件定义;软性约束,使用场景有限。
。h
extern std::vector<int> v; //全局对象声明,别的源文件定义
。cpp
std::vector<int> v;
pragma once
等于条件编译,有多个头文件时,只展开一份。
饿汉模式
- 就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。(在 main 函数之前就创建初始化)
- 优点:简单
- 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例存在依赖关系时,启动顺序不确定。
- 启动慢:如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。
实现:
- 构造函数私有化,不能随意创建对象
- 声明一个私有的静态类型对象
- 写一个获取初私有对象的静态函数
- 声明与定义分开
。h
class MyClass
{
public:
//3. 写一个获取初始化的静态函数
static MyClass& get_init();
vector<int> _vi;//想把vector对象私有,再包一层对象的插入等等
/*void PushBcak(size_t x)
{
return _vi.push_back(x);
}*/
private:
//1.构造函数私有化,不能随意创建对象
MyClass(){}
//防拷贝
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
//2.声明一个私有的静态类型对象
static MyClass _my;
};
.cpp
MyClass MyClass::_my;
MyClass& MyClass::get_init()
{
return _my;
}
test:
int main()
{
MyClass::get_init()._vi.push_back(222);
MyClass::get_init()._vi.push_back(24);
MyClass::get_init()._vi.push_back(34);
for (auto& i : MyClass::get_init()._vi)
{
cout << i << endl;
}
return 0;
}
懒汉模式
- 优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
- 缺点:复杂
实现:
- 构造函数私有化,不能随意创建对象
- 声明一个私有的静态类型指针对象
- 写一个获取私有对象的静态函数
- 声明与定义分开
- 要加双层锁,保证线程安全和效率;避免线程数据被覆盖、线程频繁上下文切换和阻塞。
。h
class MyClass //静态成员函数,成员变量 声明定义分开
{
public:
//3. 写一个获取初始化的静态函数
static MyClass* get_init();
vector<int> _vi;//想把vector对象私有,再包一层对象的插入等等
/*void PushBcak(size_t x)
{
return _vi.push_back(x);
}*/
private:
//1.构造函数私有化,不能随意创建对象
MyClass() {}
//放拷贝
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
//2.声明一个私有的静态类型指针对象
static MyClass* _my;
static mutex _tex;//互斥锁
};
,cpp
MyClass* MyClass::_my = nullptr;
mutex MyClass::_tex;
MyClass* MyClass::get_init()
{
if (_my==nullptr)//双锁保证了线程效率,避免了线程频繁的上下文切换,来回阻塞
{
_tex.lock();
if (_my == nullptr)
{
_my = new MyClass;
}
_tex.unlock(); //枷锁保证线程安全,避免数据被覆盖。
}
return _my;
}
懒汉模式测试用例
源1.cpp
void f1()
{
MyClass* pm = MyClass::get_init();
pm->_vi.push_back(21);
pm->_vi.push_back(22);
pm->_vi.push_back(23);
cout << &pm->_vi << endl;
for (auto& i : pm->_vi)
{
cout << i << endl;
}
cout << endl;
}
源.cpp
int main()
{
f1();
MyClass* pm = MyClass::get_init();
pm->_vi.push_back(24);
pm->_vi.push_back(25);
pm->_vi.push_back(26);
cout << &pm->_vi << endl;
for (auto& i : pm->_vi)
{
cout << i << endl;
}
//MyClass1 m1 = MyClass1::get_init();
return 0;
}
单例模式对象的销毁
- 饿汉模式,由于对象是静态的,想释放也释放不了,main函数结束以后,意味着整个进程就结束了,系统会对整个进程的资源进行清理。
- 懒汉模式,同上,不用手动释放问题不大:手动如下:
void MyClass::Delmc()
{
if (_my != nullptr)//双锁保证了线程效率,避免了线程频繁的上下文切换,来回阻塞
{
_tex.lock();
if (_my!=nullptr)
{
delete _my;
_my = nullptr;
}
_tex.unlock(); //枷锁保证线程安全,避免数据被覆盖。
}
}