C++特殊类的设计

1.设计一个类,这个类不能拷贝

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

  • C++98
    将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
class CopyBan
{
    // ...
private:
	//只定义不声明
    CopyBan(const CopyBan&);
    CopyBan& operator=(const CopyBan&);
    //...
};

原因:

  1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不
    能禁止拷贝了
  2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写
    反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
  • C++11
    C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
class CopyBan
{
    // ...
    CopyBan(const CopyBan&)=delete;
    CopyBan& operator=(const CopyBan&)=delete;
    //...
};

2. 设计一个类,这个类只能在堆上创建对象

实现方式:

  1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
  2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
class HeapOnly
{
public:
    template <class... Args>
    static HeapOnly* CreateObject(Args&&...args)
    {
        return new HeapOnly(args...);
    }

    HeapOnly(const HeapOnly&) = delete;
    HeapOnly& operator=(const HeapOnly&) = delete;
private:
    HeapOnly(int a, int b) { x = a; y = b; }
    int x;
    int y;
};

方法二:禁用析构函数

class HeapOnly
{
public:
    template <class... Args>
    static HeapOnly* CreateObject(Args&&...args)
    {
        return new HeapOnly(args...);
    }

    HeapOnly(const HeapOnly&) = delete;
    HeapOnly& operator=(const HeapOnly&) = delete;
    HeapOnly(int a, int b) { x = a; y = b; }
    HeapOnly() {};

    void Destroy()
    {
        delete this;
    }

private:
    ~HeapOnly() 
    {
        cout << "~HeapOnly() " << endl;
    };
    int x;
    int y;
};

int main()
{
    HeapOnly* p = HeapOnly::CreateObject(1, 1);
    p->Destroy();

    //因为这里shared_ptr默认定制删除器delete,但是这里delete调用不了,所以要自己定制一个
    shared_ptr<HeapOnly> p2(new HeapOnly, [](HeapOnly* ptr) {ptr->Destroy(); });
    return 0;
}

3. 设计一个类,这个类只能在栈上创建对象

方法同上将构造函数私有化,然后设计静态方法创建对象返回即可。

class StackOnly
{
public:
    template <class... Args>
    static StackOnly CreateObject(Args&&...args)
    {
        return StackOnly();
    }

    //StackOnly(const StackOnly&) = delete;//这里就不能把拷贝构造给禁用,因为CreateObject是值返回,需要拷贝构造
    StackOnly& operator=(const StackOnly&) = delete;

    //解决这种StackOnly* p2 = new StackOnly(p);
    //因为如果不重载new的话,就可以使用new创建出一个在堆上的类,而new使用的是全局的new,但是这里如果我们自己重载一个的话它就会使用我们重载出来的了。
    void* operator new(size_t size) = delete;
    void operator delete(void* p) = delete;
private:
    StackOnly() { }
};
int main()
{
    StackOnly p = StackOnly::CreateObject();

    return 0;
}

4. 设计一个类,这个类不能被继承

  • C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
    因为继承对象需要调用被继承对象的构造
  • C++11方法final关键字,final修饰类,表示该类不能被继承。
class A  final
{
    // ....
};

5. 设计一个类,这个类只能被创建一次 (单例模式)

设计模式:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

单例模式有两种实现模式:

  • 饿汉模式
    就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
class Singleton
{
public:
	//因为只有static的函数才能调用static的成员
	static Singleton* CtreaSing()
	{
		return &_sign;
	}

	void Print()
	{
		cout << _x << endl;
		cout << _y << endl;
		for (auto& x : _vstr)
		{
			cout << x << " ";
		}
		cout << endl;
	}

	void Add(const string& str)
	{
		_vstr.push_back(str);
	}

	//同时讲拷贝构造和赋值构造禁止
	Singleton(const Singleton& s) = delete;
	Singleton& operator= (const Singleton & s) = delete;
private:
	Singleton(int x = 0, int y = 0, const vector<string>& vstr = {"xxx", "yyy"})
		:_x(x),
		_y(y),
		_vstr(vstr)
	{}
	int _x;
	int _y;
	vector<string> _vstr;

	//声明一个静态的类对象
	//注意这里不存在递归式的声明和创建,因为这里是被static修饰的成员,被static修饰的成员是不存在类中的而是存放在静态区的
	static Singleton _sign;
};
//类内声明,类外定义
//此时当运行是这个对象就被常见了,而且只有一份
Singleton Singleton::_sign(1, 1, {"hehe", "haha"});

int main()
{
	Singleton::CtreaSing()->Print();
	Singleton::CtreaSing()->Add("2222");
	Singleton::CtreaSing()->Print();

	return 0;
}

在这里插入图片描述
饿汉的缺点

  1. 因为饿汉模式是一来就创建对象,也就是说一来就是初始化,这样做的话如果这个类非常的大构造成本开销就很大,那么如果某个 程序使用了这个方法的话,那么这个程序的启动也会受到影响。
  2. 如果多个单例存在依赖关系的话,会无法控制。比如存在A和B两个单例类,现在要求先构造A后构造B,而这是无法控制的,因为他们两个都是单例类,都是全局静态的,谁先构造无法得知。
  • 懒汉模式
class Singleton
{
public:
	//因为只有static的函数才能调用static的成员
	static Singleton* CtreaSing()
	{
		//调用的时候初始化
		if (_psign == nullptr)
		{
			_psign = new Singleton;
		}
		return _psign;
	}

	static void DeleteSing()
	{
		if (_psign)
		{
			delete _psign;
			_psign = nullptr;
		}
	}

	void Print()
	{
		cout << _x << endl;
		cout << _y << endl;
		for (auto& x : _vstr)
		{
			cout << x << " ";
		}
		cout << endl;
	}

	void Add(const string& str)
	{
		_vstr.push_back(str);
	}

	//同时讲拷贝构造和赋值构造禁止
	Singleton(const Singleton& s) = delete;
	Singleton& operator= (const Singleton& s) = delete;
private:
	Singleton(int x = 0, int y = 0, const vector<string>& vstr = { "xxx", "yyy" })
		:_x(x),
		_y(y),
		_vstr(vstr)
	{
		cout << "lazy Singleton" << endl;
	}
	int _x;
	int _y;
	vector<string> _vstr;

	//我可以声明一个指针,这样子当创建的时候我们在初始化
	static Singleton* _psign;

	~Singleton()
	{
		cout << "~Singleton()" << endl;
	}

	//为了防止忘记delete可以用一个类包起来,这样这个类出了作用域就会调用析构
	//内部类
	class GC
	{
	public:
		~GC()
		{
			Singleton::DeleteSing();
		}
	};
	static GC gc;
};
//类内声明,类外定义
//此时当运行是这个对象就被常见了,而且只有一份
Singleton* Singleton::_psign = nullptr;
Singleton::GC Singleton::gc;

在这里插入图片描述

但是C++11后懒汉就可以得到一个优化:

class Singleton
{
public:
	//因为只有static的函数才能调用static的成员
	static Singleton* CtreaSing()
	{
		//局部的静态变量,会在第一次调用函数的时候构造初始化
		//C++11后才可以这样写
		//C++11之前无法保证线程安全问题——线程我们后序话进行讲解
		static Singleton sign;
		return &sign;
	}

	void Print()
	{
		cout << _x << endl;
		cout << _y << endl;
		for (auto& x : _vstr)
		{
			cout << x << " ";
		}
		cout << endl;
	}

	void Add(const string& str)
	{
		_vstr.push_back(str);
	}

	//同时讲拷贝构造和赋值构造禁止
	Singleton(const Singleton& s) = delete;
	Singleton& operator= (const Singleton& s) = delete;
private:
	Singleton(int x = 0, int y = 0, const vector<string>& vstr = { "xxx", "yyy" })
		:_x(x),
		_y(y),
		_vstr(vstr)
	{
		cout << "lazy Singleton" << endl;
	}
	int _x;
	int _y;
	vector<string> _vstr;

	//我可以声明一个指针,这样子当创建的时候我们在初始化
	static Singleton* _psign;

	~Singleton()
	{
		if (_psign)
		{
			delete _psign;
			_psign = nullptr;
		}
		cout << "~Singleton()" << endl;
	}
};
//类内声明,类外定义
//此时当运行是这个对象就被常见了,而且只有一份
Singleton* Singleton::_psign = nullptr;
  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初阳hacker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值