只能在堆上创建对象的类
基本思路:把构造函数私有化,禁用拷贝和赋值运算符重载函数,自己定义一个静态的创建对象的专用函数
cpp
//只能在堆上创建对象的类
class TestClass {
public:
//将构造函数定义在私有区域,禁止外部调用构造函数
//使用一静态成员函数创建对象,类内声明类外定义
static TestClass& MakeNew(const int code, const string name);
//禁用拷贝构造函数和赋值运算符重载
TestClass(const TestClass& temp) = delete;
const TestClass& operator=(const TestClass& temp) = delete;
//如果成员占据动态分配的内存,就需要自己写一个,这里只是做演示
~TestClass() {
cout << "data has been release" << endl;
}
private:
//构造函数虽然在private中但是仍可以被类内函数调用
TestClass(const int code, const string name):_code(code),
_name(name){}
int _code;
string _name;
};
//需要返回一个实际中的对象
TestClass& TestClass::MakeNew(const int code,const string name ) {
TestClass* temp = new TestClass(code, name);//makenew是类内函数可以调用构造函数
return *temp;
}
只在栈上创建对象的类
基本思路:把构造函数私有化,同时禁用new和delete运算符重载,直接保证了用户不可能在堆上创建类。此时拷贝和赋值得到的对象也只能存在于栈上,所以不需要禁用
cpp
//只能在栈上创建对象的类
class StackOnlyClass {
public:
//将构造函数定义在私有区域,禁止外部调用构造函数
//使用一静态成员函数创建对象,类内声明类外定义
static StackOnlyClass& MakeNew2(const int code, const string name);
//不需要禁用拷贝构造函数和赋值运算符重载,因为new已经被禁止,动态内存分配的可能性为0,所有拷贝和赋值只能在栈上进行
//这里需要禁用new和delete才能完全避免在堆上创建
//注:void*是万能指针
void* operator new(size_t n) = delete;
void operator delete(void* p) = delete;
//如果成员占据动态分配的内存且没有析构函数,就需要配套写一个
~StackOnlyClass() {
cout << "data has been release" << endl;
}
private:
StackOnlyClass(const int code, const string name) :_code(code),
_name(name) {
}
int _code;
string _name;
};
StackOnlyClass& StackOnlyClass::MakeNew2(const int code, const string name) {
StackOnlyClass temp(code, name);
return temp;
}
单例模式
一个类只能创建一个对象,即单例模式,该模式可以保证进程中该类只有一个实例
单例模式有两种实现方式:饿汉模式和懒汉模式
饿汉模式
cpp
//饿汉模式
class SingleClass {
public:
static SingleClass& MakeSingleClass(int code, string name) {
_theone._code = code; _theone._name = name;
return _theone;//外部想要创建对象时,永远只返回那个已经被定义的类对象
}
private:
SingleClass(){}
SingleClass(const SingleClass& input) = delete;
SingleClass& operator=(const SingleClass& input) = delete;
int _code;
string _name;
static SingleClass _theone;//类内声明类外定义,这个对象在全局只有一个
};
SingleClass SingleClass::_theone;
问题1:明明构造函数已经放在private中了,还可以在类内声明类外定义?
首先**_theone在类内**,所以可以调用私有构造函数
因为**_theone是被static修饰的成员** ,所以在类外可以用类名::成员名的方式找到。
问题2. 为什么函数MakeSingleClass要用static修饰?
为了在没有对象时就能调用它。 单例模式把构造函数私有化了,外部无法通过 new SingleClass创建对象,既然没有对象,就无法调用普通的成员函数,只有 static 函数属于类本身 ,不依赖对象,可直接调用。
static成员函数不能访问非静态成员,但它可以访问类的私有静态成员变量 (_theone),从而完成初始化并返回地址。
问题3. SingleClass SingleClass::_theone为什么不算重定义?
类内只是"声明",类外才是"定义"。而 类外定义的语法是 类型 类名::变量名。
饿汉模式有如下缺陷:
- 如果单例对象的初始化内容很多,就会影响程序的启动速度 。代码的东西并不是迟早都要加载的,比如一个导出 PDF的功能类,初始化很重,但用户可能只使用了预览功能,从未点击导出。那饿汉式启动时白白浪费了初始化时间。
- 假如两个单例对象有依赖关系,如B需要A先初始化,饿汉模式无法保证先后关系,比如A,B在两个CPP文件中,谁先谁后?
懒汉模式
懒汉模式可以避免饿汉模式的缺陷,懒汉模式代码如下:
cpp
//懒汉模式
class LazyClass {
public:
static LazyClass& MakeLazyClass(int code, string name) {
_theone = new LazyClass;
(*_theone)._code = code; (*_theone)._name = name;
return *_theone;
}
static void DeleteLazyClass() {
delete _theone;
cout << "懒汉被消除" << endl;
_theone = nullptr;
}
//自动化析构
class GC {
public:
~GC() {
DeleteLazyClass();
}
};
private:
LazyClass() {}
~LazyClass() {}
LazyClass(const LazyClass& input) = delete;
LazyClass& operator=(const LazyClass& input) = delete;
int _code;
string _name;
static GC gc;
static LazyClass* _theone;
};
LazyClass::GC LazyClass::gc;
LazyClass* LazyClass::_theone;//只定义一个指针,后面分配好内存可以拿来用
懒汉模式在类外定义类指针而不是类对象,无论程序用不用得上都只占用4字节,用户实际需要使用单例对象的时候就调用MakeLazyClass然后进行动态内存分配。
另外定义一个内部类GC以及GC类对象gc,当这个单例对象生命周期结束的时候,会自动调用GC的析构函数进而调用我们为单例对象定制的空间释放函数DeleteLazyClass
问题:为什么不直接写一个析构函数?
cpp
~LazyClass(){
delete _theone;//_theone本来就是LazyClass类型,那就无限循环调用析构了
}
其它有关static的问题
类内声明类内定义和类内声明类外定义的区别:
类内定义编译器会把该定义当作内联函数 处理。后果是一旦 修改了函数体里的代码,所有包含这个头文件的 .cpp 文件都要重新编译。如果项目很大,改一行代码可能要编译几分钟。
2. 类外定义实现声明和实现分离。假如 修改 .cpp 里的逻辑,只需要重新编译这一个文件,然后链接即可。编译速度快且代码结构更清晰(接口与实现分离)。
总结:类内定义 :适合极短的、简单的、不需要频繁修改的代码(如 getter/setter)。类外定义 :适合复杂的逻辑,是工程开发的标准做法。
static在 C++ 类中的特性如下:
