1. 请设计一个 不能拷贝 的类
C++ 98:
template<class T>
class Copy_Ban
{
private:
Copy_Ban(Copy_Ban<T>& cb);
Copy_Ban<T>& operator=(Copy_Ban<T>& cb);
};
将 拷贝构造 和 赋值重载 只声明不定义且私有化。
C++ 11:
template<class T>
class Copy_Ban
{
public:
Copy_Ban(Copy_Ban<T>& cb) = delete;
Copy_Ban<T>& operator=(Copy_Ban<T>& cb) = delete;
};
error C2280: “Copy_Ban::Copy_Ban(Copy_Ban &)”: 尝试引用已删除的函数
2. 请设计一个 只能在堆上创建对象 的类
核心步骤只有 3 条:
- 将构造私有化
- 通过特定接口创建对象
- 将 拷贝构造 和 赋值重载 封死
class HeapOnly
{
public:
// 2. 通过特定接口创建对象
template<class... Args>
static HeapOnly* GetInstance(Args... args) // static 非静态成员函数(不需要使用 this)
{
return new HeapOnly(args...);
}
// 3. 将 拷贝构造 和 赋值重载 封死
HeapOnly(const HeapOnly&) = delete;
HeapOnly& operator=(const HeapOnly&) = delete;
// ...
private:
// 1. 将构造函数私有化
HeapOnly(int x)
:_data(x)
{}
HeapOnly() {}
int _data;
vector<int> _v;
};
创建对象:
HeapOnly* ho = HeapOnly::GetInstance();
// 可以通过 ho 调用类内部的各种函数
3. 请设计一个 只能在栈上创建对象 的类
- 将构造私有化
- 通过特定接口创建对象
- 将 operator new (operator delete)封死
class StackOnly
{
public:
template<class... Args>
static StackOnly GetInstance(Args... args)
{
return StackOnly(args...);
}
// 固定写法
void* operator new(size_t) = delete;
void operator delete(void*) = delete;
private:
StackOnly(int x)
:_data(x)
{}
StackOnly()
{}
int _data;
vector<int> _v;
};
创建对象:
StackOnly so = StackOnly::GetInstance();
何时需要删除 operator=
?
- 你的类被设计成不可复制的(既不能被拷贝构造,也不能被赋值)。
- 你的类包含了一些不允许复制的成员,因为这些成员类型可能自己就禁止了拷贝和赋值。
4. 请设计一个 不能被继承 的类
C++ 98:
将构造函数私有化,派生类(子类)无法调用基类(父类)的构造函数,则无法被继承。
struct A
{
private:
A() {}
};
struct B : public A
{
public:
B() {} // error C2248: “A::A”: 无法访问 private 成员(在“A”类中声明)
};
C++ 11:
使用关键词 final
修饰类。
struct A final
{
public:
A() {}
};
struct B : public A // error C3246: “B”: 无法从“A”继承,因为它已被声明为“final”
{
public:
B() {}
};
5. 请设计 只能创建一个对象 的类(单例模式)(不加锁)
单例模式: 一个类只能创建一个对象,并提供一个全局访问点来获取该实例。
- 饿汉模式:单例对象在类加载时就会被创建。 单例对象的生命周期与程序的生命周期相同,从程序开始运行到程序运行结束,单例对象始终存在。
缺点:1. 单例对象一旦被创建,就会一直占用内存。
2. 如果单例对象创建时需要执行耗时的操作,会影响程序的启动速度 —— 单例对象在 main 函数开始之前被创建。
namespace hunger
{
class Singleton
{
public:
// 提供全局访问点来获取实例
static Singleton* GetInstance()
{
return &_sint;
}
// 删除拷贝构造和赋值运算符重载,防止复制
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
// 私有化构造函数
Singleton() {}
// 静态成员变量保存实例
static Singleton _sint;
};
// 初始化静态成员变量
Singleton Singleton::_sint;
}
- 懒汉模式:单例对象在第一次被请求时才创建,节省资源。
缺点: 1. 在多线程环境中,如果没有正确的线程同步机制,可能会创建多个实例,破坏单例特性。
(未加锁版本)
namespace lazy
{
class Singleton
{
public:
static Singleton* GetInstance()
{
// 第一次被请求时,创建对象
if (_sint == nullptr)
{
_sint = new Singleton;
}
return _sint;
}
static void DestroyInstance()
{
if (_sint)
{
delete _sint;
_sint = nullptr;
}
}
// 删除拷贝构造和赋值运算符重载,防止复制
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
// 私有化构造函数和析构函数
~Singleton()
{
cout << "~Singleton()" < endl;
}
Singleton() {
cout << "Singleton()" << endl;
}
static Singleton* _sint;
};
// 初始化静态成员单例对象
Singleton* Singleton::_sint = nullptr;
}
上面这段代码有一个小缺点:需要手动调用 lazy::Singleton::DestroyInstance();
完成单例对象的释放。
优化一下:
// version 1
namespace lazy
{
class Singleton
{
// ,,,
};
struct GC
{
GC() {}
~GC() { Singleton::DestroyInstance(); }
};
GC gc;
}
// version 2
namespace lazy
{
class Singleton
{
// ,,,
private:
// 设计成内部私有类,用户就无法手动调用 GC 的析构函数,释放单例对象
struct GC
{
GC() {}
~GC() { Singleton::DestroyInstance(); }
};
GC gc;
};
}