C++ | 深入理解C++中的特殊类设计和单例模式(懒汉模式、饿汉模式)

目录

特殊类设计和单例模式

1、不可拷贝类

2、只能在堆上创建对象的类

3、只能在栈上创建对象的类

4、不可继承的类

5、单例模式(懒汉模式、饿汉模式)


特殊类设计和单例模式

在C++编程中,类的设计往往需要满足特定的需求和约束。特殊类设计模式提供了一种方法来实现这些需求,确保类的使用既安全又高效。本文将探讨几种常见的特殊类设计方式,包括不可拷贝类、只能在堆上创建对象的类、只能在栈上创建对象的类、不可继承的类以及单例模式。

1、不可拷贝类

在某些情况下,我们可能希望一个类的对象不能被拷贝。这可以通过以下两种方式实现:

C++98方式

在C++98中,我们可以通过只声明拷贝构造函数和赋值运算符,并将它们设置为私有成员来实现。这样,即使外部代码尝试拷贝对象,编译器也会因为访问权限问题而报错。

cpp 复制代码
// 不可拷贝类
// 将拷贝构造和赋值运算符重载delete即可
class uncopy
{
private:
    int _a;
    uncopy(const uncopy& uc);
    uncopy& operator=(const uncopy& uc);
public:
    uncopy(int a = 0)
        :_a(a)
    {}

};

C++11方式

C++11引入了delete关键字,可以直接在类声明中删除默认的拷贝构造函数和赋值运算符,使得编译器在尝试使用这些函数时直接报错。

cpp 复制代码
// 不可拷贝类
// 将拷贝构造和赋值运算符重载delete即可
class uncopy
{
private:
    int _a;
public:
    uncopy(int a = 0)
        :_a(a)
    {}

    uncopy(const uncopy& uc) = delete;
    uncopy& operator=(const uncopy& uc) = delete;

};
2、只能在堆上创建对象的类

有时,我们希望类的实例只能在堆上创建,这可以通过以下步骤实现:

  1. 将构造函数声明为私有
  2. 提供一个公共的静态成员函数,用于在堆上分配和初始化对象。

或者:

  1. 将析构函数声明为私有
  2. 提供公共的静态成员函数,用来调用析构函数销毁对象
cpp 复制代码
// 只能在堆上创建对象的类
// 隐藏构造函数 开放静态获取接口 将拷贝构造函数delete
class Heap_only1
{
private:
    int _a;
    Heap_only1(int a = 0)
        :_a(a)
    {}
    Heap_only1(const Heap_only1& ho) = delete;
public:
    static Heap_only1* create_obj(int a)
    {
        return new Heap_only1(a);
    }
};

// 利用局部对象自动调用析构函数 隐藏析构函数 开放销毁接口 将拷贝构造函数delete
class Heap_only2
{
private:
    int _a;
    Heap_only2(const Heap_only2& ho) = delete;
    ~Heap_only2()
    {}
public:
    Heap_only2(int a = 0)
        :_a(a)
    {}
    
    static bool delete_Heap_only(Heap_only2* p)
    {
        if (p)
        {
            delete p;
            p = nullptr;
            return true;
        }
        return false;
    }
};
3、只能在栈上创建对象的类

与只能在堆上创建对象的类相反,有时我们希望类的实例只能在栈上创建。这可以通过禁用operator newoperator delete来实现。

cpp 复制代码
// 只能在栈上创建对象的类
// 将operator new和operator delete封住 提供传值返回接口获取栈上对象
// 但是 由于传值返回保留了拷贝构造函数 所以static拷贝构造无法完全封死
class stack_only
{
private:
    int _a;
    void* operator new(size_t size) = delete;
    void operator delete(void* p) = delete;
    stack_only()
    {}
public:
    static stack_only create_stack_only()
    {
        return stack_only();
    }
};
4、不可继承的类

在某些情况下,我们可能希望阻止其他类继承特定的类。这可以通过以下两种方式实现:

C++98方式

在C++98中,我们可以通过将构造函数声明为私有来实现,这样派生类就无法访问基类的构造函数。

cpp 复制代码
class NonInherit {
private:
    NonInherit() {}
};

C++11方式

C++11引入了final关键字,可以直接在类声明中使用final来阻止继承。

cpp 复制代码
class NonInherit final {
    // ...
};
5、单例模式(懒汉模式、饿汉模式)

单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。单例模式有两种实现方式:饿汉模式和懒汉模式。

饿汉模式

饿汉模式在程序启动时就创建实例,简单但可能导致启动延迟。

cpp 复制代码
//单例模式
//饿汉模式
#include<iostream>
using namespace std;
class once1
{
private:
    once1()
    {}

    static once1* a;
    once1(const once1&) = delete;
    once1& operator=(const once1&) = delete;

public:
    static once1* getObj()
    {
        return a;
    }
};
once1* once1::a = new once1;

int main()
{
    once1* p1 = once1::getObj();
    cout << p1 << endl;

    return 0;
}

懒汉模式

懒汉模式在第一次使用时才创建实例,启动无负载。

[Warning] 以下为不考虑线程安全的版本

cpp 复制代码
//懒汉模式
class once2
{
private:
    static once2* ret;
    once2()
    {}
    once2(const once2&) = delete;
    once2& operator=(const once2&) = delete;
public:
    static once2* getObj()
    {
        if (ret == nullptr)
        {
            ret = new once2;
        }
        return ret;
    }
};
once2* once2::ret = nullptr;


int main()
{
    once2* p2 = once2::getObj();
    cout << p2 << endl;

    return 0;
}

如果要求在程序退出时销毁单例对象,可以在once类中定义内部类,在内部类的析构函数中完成资源释放,程序结束时自动调用内部类的析构函数。

以懒汉模式为例:

cpp 复制代码
//懒汉模式
class once2
{
private:
	static once2* ret;
	once2()
	{}
	once2(const once2&) = delete;
	once2& operator=(const once2&) = delete;
	class del
	{
	public:
		~del()
		{
			once2::delObj();
		}
	};
	static del D;
public:
	static once2* getObj()
	{
		if (ret == nullptr)
		{
			ret = new once2;
		}
		return ret;
	}
	static void delObj()
	{
		if (ret)
		{
			delete ret;
			ret = nullptr;
			cout << "delete[]" << endl;
		}
	}
};
once2* once2::ret = nullptr;
once2::del once2::D;

int main()
{
	//once1* p1 = once1::getObj();
	//cout << p1 << endl;
	once2* p2 = once2::getObj();
	cout << p2 << endl;

	return 0;
}
相关推荐
Hello.Reader2 分钟前
Go-Redis 入门与实践从连接到可观测,一站式掌握 go-redis v9**
开发语言·redis·golang
007php00712 分钟前
使用LNMP一键安装包安装PHP、Nginx、Redis、Swoole、OPcache
java·开发语言·redis·python·nginx·php·swoole
枯萎穿心攻击19 分钟前
响应式编程入门教程第五节:Unity 生命周期与资源管理中的响应式编程
开发语言·unity·架构·c#·游戏引擎
Mr_Xuhhh25 分钟前
Qt窗口(2)-工具栏
java·c语言·开发语言·数据库·c++·qt·算法
艾莉丝努力练剑1 小时前
【数据结构与算法】数据结构初阶:详解顺序表和链表(五)——双向链表
c语言·开发语言·数据结构·学习·算法
算法_小学生1 小时前
Hinge Loss(铰链损失函数)详解:SVM 中的关键损失函数
开发语言·人工智能·python·算法·机器学习·支持向量机
YUJIANYUE1 小时前
纯前端html实现图片坐标与尺寸(XY坐标及宽高)获取
开发语言·前端·javascript
kyle~2 小时前
C++---cout、cerr、clog
开发语言·c++·算法
HHRL-yx2 小时前
C++网络编程 4.UDP套接字(socket)编程示例程序
网络·c++·udp
thginWalker3 小时前
拓扑排序/
java·开发语言