C++特殊类设计

只能在堆上创建对象的类

基本思路:把构造函数私有化,禁用拷贝和赋值运算符重载函数,自己定义一个静态的创建对象的专用函数

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为什么不算重定义?

类内只是"声明",类外才是"定义"。而 类外定义的语法是 类型 类名::变量名。


饿汉模式有如下缺陷:

  1. 如果单例对象的初始化内容很多,就会影响程序的启动速度 。代码的东西并不是迟早都要加载的,比如一个导出 PDF的功能类,初始化很重,但用户可能只使用了预览功能,从未点击导出。那饿汉式启动时白白浪费了初始化时间
  2. 假如两个单例对象有依赖关系,如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++ 类中的特性如下:

相关推荐
Season4501 小时前
C/C++的类型转换
c语言·开发语言·c++
再玩一会儿看代码1 小时前
Token 统计中的“命中缓存”和“未命中缓存”是什么意思?
经验分享·学习·缓存·电脑
明日清晨1 小时前
有符号与无符号数转换
c++
守护安静星空1 小时前
交流桩学习-控制导引
学习
是wzoi的一名用户啊~1 小时前
Floyd 模版 弗洛伊德算法 模版
c++·算法·动态规划·图论·floyd
gqk011 小时前
C++ / MFC / Qt / C# 核心知识点汇总笔记
c++·qt·mfc
晓梦林1 小时前
Fuzzz靶场学习笔记
笔记·学习·安全·web安全
计算机安禾2 小时前
【c++面向对象编程】第4篇:类与对象(三):拷贝构造函数与深浅拷贝问题
开发语言·c++·算法
网安Ruler2 小时前
安卓逆向入门到入狱学习2
android·学习