Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++修炼之路】33.特殊类设计

【C++修炼之路】33.特殊类设计

作者头像
每天都要进步呀
发布于 2023-10-16 03:14:07
发布于 2023-10-16 03:14:07
18900
代码可运行
举报
文章被收录于专栏:C++/LinuxC++/Linux
运行总次数:0
代码可运行

掌握常见特殊类的设计方式

一.设计一个类,不能被拷贝

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

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

原因:

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

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

1. 普通类的创建对象

普通的类,可以在三种位置上创建对象:

  1. 静态区
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
class HeapOnly
{};
int main()
{
	HeapOnly hp1;//栈
	HeapOnly* php2 = new HeapOnly;//堆
	static HeapOnly hp3;//静态区
	return 0;
}

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

要想只能在堆上创建对象,那一定需要在构造函数上动手脚,因为构造函数默认在栈上创建对象。

实现方式:

  1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
  2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
class HeapOnly
{
public:
	static HeapOnly* CreateObject()
	{
		return new HeapOnly;
	}
private:
	HeapOnly()
    {}
};

int main()
{
	HeapOnly* php = HeapOnly::CreateObject();
	return 0;
}

为什么要加上static?

如果CreateObject不加上static,那么在调用该方法就需要在存在对象的基础上才能使用该方法,而该对象默认一定会用构造函数,但是构造函数已经私有化,这就是一个先有鸡还是先有蛋的问题,因此一定要加上static。

但是就目前的情况,仍然可能在栈上开辟对象,首先友元一定是可以的。其次,拷贝构造函数没有显示化调用会默认生成,因此,如下方式仍可以在栈上创建对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
	HeapOnly* php2 = HeapOnly::CreateObject();
	HeapOnly php3(*php2);//栈上创建对象
	return 0;
}

所以,拷贝构造函数同样需要禁掉,才是只能在堆上创建的类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;
class HeapOnly
{
public:
	static HeapOnly* CreateObject()
	{
		return new HeapOnly;
	}
private:
	HeapOnly() {}
	HeapOnly(const HeapOnly&) = delete;
};

int main()
{
	HeapOnly* php2 = HeapOnly::CreateObject();
	return 0;
}

只在堆上创建类的第二种方式:析构私有化

如果析构私有化,那么直接创建对象会显示没有合适的构造函数,从而无法在栈上创建对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class HeapOnly
{
public:
	HeapOnly()
	{}
private:
	~HeapOnly()
	{}

	HeapOnly(const HeapOnly&) = delete;
};

int main()
{
	HeapOnly hp1;
	return 0;
}

但此时可以在堆上创建,那么此时分为如下步骤:

  1. 析构函数私有化
  2. 构造函数public显示调用
  3. 新增Destory方法,用来释放堆空间
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class HeapOnly
{
public:
	HeapOnly()
	{}
	void Destory()
	{
		this->~HeapOnly();
	}
private:
	~HeapOnly()
	{}

	HeapOnly(const HeapOnly&) = delete;
};

int main()
{
	HeapOnly* php1 = new HeapOnly;
	php1->Destory();

	return 0;
}

Destory对于static没有要求,用不用static修饰完全是我们自己所决定的。

注:在vs2019中,上面的this必须显示调用才没有错误。

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

方法一:(同上)

  1. 将构造函数私有化。
  2. 然后设计静态方法创建对象返回即可。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//请设计一个类,只能在栈上创建对象
class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		return StackOnly();
	}
private:
	StackOnly()
	{}

};
int main()
{
	StackOnly so1 = StackOnly::CreateObj();
	// 下面两种静态区和堆的位置都不能创建
	//static StackOnly so2;
	//StackOnly* pso3 = new StackOnly;
	return 0;
}

实际上,这种方法也没有彻底的封死,下面这种方式仍然可以在静态区创建:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
	static StackOnly so2 = StackOnly::CreateObj();
	return 0;
}

解决这个问题的方式:

这里设计到的强制类型转换,强制类型转换中间会生成一个临时对象,将这个临时对象拷贝给需要定义的对象,若把拷贝构造封住,那么不仅这个会报错,前面的也会报错,因为前者的赋值也是将返回的对象临时拷贝:

因此,没有什么很好的办法去完全的封死。但硬要封死,即把拷贝构造封住,那就不要用 = 获取,而是直接调用,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//请设计一个类,只能在栈上创建对象
class StackOnly
{
public:
	static StackOnly&& CreateObj()
	{
		return StackOnly();
	}

	void Print() const
	{
		cout << "StackOnly::Print()" << endl;
	}
private:
	StackOnly()
	{}
	StackOnly(const StackOnly&) = delete;

};
int main()
{
	/*StackOnly so1 = StackOnly::CreateObj();
	static StackOnly so2 = StackOnly::CreateObj();*/

	StackOnly::CreateObj().Print();
	const StackOnly& so4 = StackOnly::CreateObj();
	so4.Print();
	return 0;
}

目的就是防止拷贝。

ps,由于StackOnly()是局部对象,出了作用域被销毁,因此采用右值引用才可以传出。


方法二:封注operator newoperator delete

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class StackOnly
{
public:
    StackOnly()
	{}
	static StackOnly CreateObj()
	{
		return StackOnly();
	}
	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
	
};

这种方式同样可以,因为在new对象的过程中,一定会存在operator new的步骤。但是这种方法只能封住堆上的,却无法封住静态的。

所以最好的方式就是用方式一。

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

  • C++98方式
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
    static NonInherit GetInstance()
    {
        return NonInherit();
    }
private:
    NonInherit()
    {}
};
  • C++11方法

final关键字,final修饰类,表示该类不能被继承。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A  final
{
  // ....
};

五.单例模式

  • 单例模式:只能创建一个对象。

1.什么是设计模式?

设计模式是在软件工程中经过反复实践证明的一套解决问题的经验总结,用于解决常见的设计问题。以下是一些常见的设计模式:

  1. 创建型模式(Creational Patterns):
    • 工厂方法模式(Factory Method Pattern)
    • 抽象工厂模式(Abstract Factory Pattern)
    • 单例模式(Singleton Pattern)
    • 建造者模式(Builder Pattern)
    • 原型模式(Prototype Pattern)
  2. 结构型模式(Structural Patterns):
    • 适配器模式(Adapter Pattern)
    • 桥接模式(Bridge Pattern)
    • 组合模式(Composite Pattern)
    • 装饰者模式(Decorator Pattern)
    • 外观模式(Facade Pattern)
    • 享元模式(Flyweight Pattern)
    • 代理模式(Proxy Pattern)
  3. 行为型模式(Behavioral Patterns):
    • 观察者模式(Observer Pattern)
    • 状态模式(State Pattern)
    • 策略模式(Strategy Pattern)
    • 命令模式(Command Pattern)
    • 职责链模式(Chain of Responsibility Pattern)
    • 迭代器模式(Iterator Pattern)
    • 中介者模式(Mediator Pattern)
    • 备忘录模式(Memento Pattern)
    • 访问者模式(Visitor Pattern)
    • 模板方法模式(Template Method Pattern)
  4. 并发型模式(Concurrent Patterns):
    • 信号量模式(Semaphore Pattern)
    • 线程池模式(Thread Pool Pattern)
    • 读写锁模式(Read-Write Lock Pattern)
    • 生产者消费者模式(Producer-Consumer Pattern)

以上仅是一些常见的设计模式,实际上还有其他的设计模式。每个设计模式都有特定的应用场景和解决问题的方式。请注意,在使用设计模式时,应根据具体的需求和情况来选择适当的设计模式。

比如迭代器模式,把复杂的东西给封装好,使用时就可以避免接触复杂的底层结构。 比如配接器模式等等,也是这个意思。

使用设计模式的目的: 为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

2.单例模式

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

由于全局对象只能有一个,换句话说是获取这个对象,那就需要对构造函数进行操作。

单例模式有两种实现模式:饿汉模式、懒汉模式

饿汉模式:不管你将来用不用,程序启动时就创建一个唯一的实例对象。

饿汉模式的条件:main函数之前就初始化

设计饿汉模式的步骤:

  1. 将构造函数设成private,以及封死拷贝构造和重载赋值
  2. 定义成员变量,变量类型为static 类型名
  3. 在类外初始化这个单例的对象
  4. 添加其它成员方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//单例模式的类:全局只有一个唯一对象
// 饿汉模式(main函数之前初始化)
// 缺点:1、单例对象初始化时对象太多,导致启动慢 
//		 2、多个单例类有初始化依赖关系,饿汉模式无法控制
class InfoSingleton
{
public:
	static InfoSingleton& GetInstance()
	{
		return _sins;
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
	static InfoSingleton _sins;
};

InfoSingleton InfoSingleton::_sins;

int main()
{
	InfoSingleton::GetInstance().Insert("张三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("张三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);
	infosl.Print();
	return 0;
}

可见在调用时可以通过引用来简化代码量。

饿汉模式的缺点:

  1. 单例对象初始化数据太多,导致启动慢
  2. 多个单例类有初始化依赖关系,饿汉模式无法控制

假设有两个单例类A和B,分别代表数据库文件系统,要求先初始化A,再初始化B,并且B会依赖A,那么此时饿汉模式就无法控制顺序。

如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。

懒汉模式

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。

懒汉模式的条件:

  1. 对象在main函数之后才会创建,不会影响启动顺序
  2. 可以主动控制创建顺序

设计懒汉模式的步骤:(与饿汉模式基本相同)

  1. 将构造函数设成private,以及封死拷贝构造和重载赋值
  2. 定义成员变量,变量类型为static 类型名
  3. 在类外初始化这个单例的对象
  4. 添加其它成员方法

与饿汉模式的区别:

  1. 对象在main函数之后才会创建,不会影响启动顺序
  2. 可以主动控制创建顺序
  3. 将对象的创建改为在堆上创建
  4. 懒汉模式存在多个对象一起调用GetInstance的情况,存在线程安全的风险,可能new出来多个对象,因此需要加锁,需要新增一个锁的成员对象,并定义为static类型;饿汉模式一开始就一个对象,不用创建,所以不用锁。

注意:锁不能被拷贝,因此定义锁的成员变量时可以用指针(地址)或者引用的方式定义,C++采用地址的行为不常见,用引用更好。

加锁也是有讲究的,如果像这样的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//多个对象一起调用GetInstance,存在线程安全的风险,可能new出来多个对象,因此需要加锁
static InfoSingleton& GetInstance()
{
    _pmtx.lock();
    if (_psins == nullptr)//避免对象new出来以后每次都加锁,提高性能
    {
        _psins = new InfoSingleton;
    }
    _pmtx.unlock();
    return *_psins;
}

由于这种方式每次都需要加锁,但实际上只有第一次创建对象才需要加锁,所以为了避免锁影响效率,使用双层if检查;此外,对于new,一旦抛异常,就需要捕获,此时可以使用try-catch,但这种写法不可行,通过之前智能指针的RAII思想,我们可以自己设定一个类:基于RAII思想的管理类,来防止锁的问题。

为什么try-catch不可行,因为还在加锁阶段,一旦进行捕获跳转,那么这把锁会一直锁住,为了避免出现这种情况,才使用RAII的思想。C++线程库中也有对应的库函数方法,但是这里仍然可以手撕一个。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//RAII的锁管理类
template<class Lock>
class LockGuard
{
public:
	LockGuard(Lock& lk)
		:_lk(lk)
	{
		lk.lock();
	}

	~LockGuard()
	{
		_lk.unlock();
	}
private:
	Lock& _lk;//成员变量用引用-->避免拷贝
};

//懒汉模式
//1、对象在main函数之后才会创建,不会影响启动顺序
//2、可以主动控制创建顺序

//问题:
class InfoSingleton
{
public:
	//多个对象一起调用GetInstance,存在线程安全的风险,可能new出来多个对象,因此需要加锁
	static InfoSingleton& GetInstance()
	{
		//第一次获取单例对象的时候创建对象
		//双检查加锁
		if (_psins == nullptr)//避免对象new出来以后每次都加锁,提高性能
		{
			// t1  t2
			LockGuard<mutex> mtx(_smtx);
			if (_psins == nullptr) //保证线程安全且只new一次
			{
				_psins = new InfoSingleton;
			}
		}

		return *_psins;
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
	static InfoSingleton* _psins;
	static mutex _smtx;
};

InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;

int main()
{
	InfoSingleton::GetInstance().Insert("张三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("张三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);
	infosl.Print();
	return 0;
}

库中也有对应的加锁方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static InfoSingleton& GetInstance()
{
    //第一次获取单例对象的时候创建对象
    //双检查加锁
    if (_psins == nullptr)//避免对象new出来以后每次都加锁,提高性能
    {
        // t1  t2
        //LockGuard<mutex> mtx(_smtx);
        std::lock_guard<mutex> lock(_smtx);//库中的方法
        if (_psins == nullptr) //保证线程安全且只new一次
        {
            _psins = new InfoSingleton;
        }
    }

    return *_psins;
}

new出来之后是否需要释放?

一般单例模式对象不需要考虑释放。单例模式的类的一个对象通常在整个程序运行期间都会使用,因此最后不delete也不会有问题,只要进程最终正常结束,对象的资源就会由OS自动释放。

什么时候单例模式的对象需要释放?

单例对象不用时,必须手动处理,一些资源需要保存。假设工资名单需要保存到文件里,要求系统结束之前将信息保存进去,此时就需要手动处理。所以,可以新增一个方法DelInstance(),是否需要调用取决于自己:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//懒汉模式
//1、对象在main函数之后才会创建,不会影响启动顺序
//2、可以主动控制创建顺序
class InfoSingleton
{
public:
	//多个对象一起调用GetInstance,存在线程安全的风险,可能new出来多个对象,因此需要加锁
	static InfoSingleton& GetInstance()
	{
		//第一次获取单例对象的时候创建对象
		//双检查加锁
		if (_psins == nullptr)//避免对象new出来以后每次都加锁,提高性能
		{
			// t1  t2
			//LockGuard<mutex> mtx(_smtx);
			std::lock_guard<mutex> lock(_smtx);
			if (_psins == nullptr) //保证线程安全且只new一次
			{
				_psins = new InfoSingleton;
			}
		}

		return *_psins;
	}

	//一般单例对象不需要考虑释放
	//单例对象不用时,必须手动处理,一些资源需要保存
	static void DelInstance()
	{
		//保存数据到文件
		// ...
		std::lock_guard<mutex> lock(_smtx);
		if (_psins)
		{
			delete _psins;
			_psins = nullptr;
		}
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
	static InfoSingleton* _psins;
	static mutex _smtx;
};

InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;

int main()
{
	InfoSingleton::GetInstance().Insert("张三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("张三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);
	infosl.Print();

	InfoSingleton::DelInstance();//主动调用
	return 0;
}

如果忘记主动调用,同样会产生错误,因此仍需要设计一个能够自动回收的方式,这里采用新增一个内部类GC,利用RAII的思想,一旦忘主动回收,其在main函数结束时就会自动回收,此时就需要新增一个成员变量以及内部类:

注:内部类是外部类的友元

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//懒汉模式
//1、对象在main函数之后才会创建,不会影响启动顺序
//2、可以主动控制创建顺序
class InfoSingleton
{
public:
	//多个对象一起调用GetInstance,存在线程安全的风险,可能new出来多个对象,因此需要加锁
	static InfoSingleton& GetInstance()
	{
		//第一次获取单例对象的时候创建对象
		//双检查加锁
		if (_psins == nullptr)//避免对象new出来以后每次都加锁,提高性能
		{
			// t1  t2
			//LockGuard<mutex> mtx(_smtx);
			std::lock_guard<mutex> lock(_smtx);
			if (_psins == nullptr) //保证线程安全且只new一次
			{
				_psins = new InfoSingleton;
			}
		}

		return *_psins;
	}

	//一般单例对象不需要考虑释放
	//单例对象不用时,必须手动处理,一些资源需要保存
	static void DelInstance()
	{
		//保存数据到文件
		// ...
		std::lock_guard<mutex> lock(_smtx);
		if (_psins)
		{
			delete _psins;
			_psins = nullptr;
		}
	}
	//忘记调用DelInstance(),自动回收
	class GC
	{
	public:
		~GC()
		{
			if (_psins)
			{
				cout << " ~GC()" << endl;
				DelInstance();
			}
		}
	};

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
	static InfoSingleton* _psins;
	static mutex _smtx;
	static GC _gc;
};

InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;
InfoSingleton::GC InfoSingleton::_gc;

int main()
{
	InfoSingleton::GetInstance().Insert("张三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("张三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);
	infosl.Print();
	return 0;
}

因此,可以主动回收,也可以在程序结束时自动回收,但单例对象一般不需要回收。

实现懒汉的另一种方式:

懒汉模式的实现还有另一种方式,直接static,也是只创建一次对象,所以下面的方式也可以,但不是一种通用的方式。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//是懒汉:因为静态的局部变量是在main函数之后才创建初始化的:局部静态变量的初始化只初始化一次。
//C++11之前,不能保证sinst的初始化是线程安全的。
//C++11之后,可以。
class InfoSingleton
{
public:
	//多个对象一起调用GetInstance,存在线程安全的风险,可能new出来多个对象,因此需要加锁
	static InfoSingleton& GetInstance()
	{
		static InfoSingleton sinst;
		return sinst;
	}


	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{
		cout << "InfoSingleton()" << endl;
	}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
};

int main()
{
	InfoSingleton::GetInstance().Insert("张三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("张三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("赵六", 11000);
	infosl.Print();
	return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-10-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
DeepSeek这么火,到底做对了什么?
DeepSeek作为近年来人工智能领域的一匹黑马,凭借其独特的技术路径和创新模式,迅速在全球范围内引起了广泛关注。其成功并非偶然,而是多种因素综合作用的结果。以下将从技术创新、开源生态构建、小团队精兵模式以及对AI技术普及的推动等方面,深入剖析DeepSeek的成功之道。
楼炜
2025/02/10
2.4K1
探秘 DeepSeek 落地进展,腾讯云携手业界专家共话 AI 生产力
踏入 2025 年,AI 技术已深度融入社会发展的脉络。DeepSeek 的崛起不仅成功唤醒了国人对 AI 的全新认知,更如一条行业的“鲇鱼”,充分激活了国内科技公司潜藏的巨大能量。此刻,AI 领域中创新与实践的中国加速度,再度毫无保留地展现在全世界眼前。那么,在 AGI 技术突破与产业智能化转型的交汇点上,哪些落地场景应用最值得我们期待?未来,这一波新的 AI 浪潮又将走向何方,为行业带来怎样长期的影响?
TVP官方团队
2025/04/04
1070
探秘 DeepSeek 落地进展,腾讯云携手业界专家共话 AI 生产力
一位投资人的硬核观察:被DeepSeek和Manus改写的AI投资范式
【编者按】2025年,腾讯科技联动AI浪潮的创新者,推出“AI未来指北”趋势预测系列,透过他们的视角共同窥探AI与人类共生的未来图景,感受新范式的磅礴力量与深远影响。
小腾资讯君
2025/03/13
1840
企业落地 AI 大模型,一个 DeepSeek 远远不够
本文整理自 InfoQ 策划的 DeepSeek 系列直播第九期节目。在直播中,极客邦科技创始人 &CEO 霍太稳对话神州数码集团首席 AI 专家谢国斌,深入探讨了 DeepSeek 爆火背后,AI 竞争格局将发生哪些变化,以及在新的格局下,AI 企业会面临哪些新的机会和挑战,企业又该如何抓住这些机遇。 谢国斌表示,随着 AI 接入门槛和成本的降低,金融、医疗、教育和汽车等行业的应用可能会率先爆发。除此之外,制造业、办公行业等也有很大的发展空间。 但对于 B 端企业而言,除了 DeepSeek 这样的开源大模型外,还需要多方面的技术进行叠加,从多个维度考虑技术的应用,比如:在部署过程中尽可能利用 GPU 算力优化,进行进行场景化、专业的小模型训练,引入私域化的知识,提供一套智能体协同框架等等。
深度学习与Python
2025/03/03
1760
企业落地 AI 大模型,一个 DeepSeek 远远不够
同样是开源模型,为什么deepseek比千问带来这么大震撼?
在人工智能领域,开源模型已经成为推动技术发展和创新的重要力量。近年来,DeepSeek和千问(通义千问)作为两大开源模型,引发了全球范围内的广泛关注。然而,二者在技术、市场、生态以及国际影响等方面的表现却有着显著差异,尤其是DeepSeek所引发的强烈反应,甚至招致美国动用国家力量进行封杀,这背后的原因值得深入探讨。
用户7353950
2025/02/05
3.4K0
同样是开源模型,为什么deepseek比千问带来这么大震撼?
深度解析deepseek
DeepSeek(深度求索)是由中国杭州深度求索人工智能基础技术研究有限公司开发的多模态AI模型体系,其母公司为知名量化投资机构幻方量化。自2023年成立以来,DeepSeek以高性能、低成本、全开源为核心竞争力,成为全球AI领域的重要参与者。其技术定位聚焦于:
是山河呀
2025/04/05
7700
【愚公系列】《高效使用DeepSeek》001-什么是DeepSeek
在杭州西子湖畔,一家名为深度求索的科技企业正在改写全球人工智能竞争格局。其打造的DeepSeek智能系统,正以"中国速度"刷新着人们对AI能力的认知。这套系统不仅能够像人类一样理解文字、图像和代码,更在金融风控、医疗诊断等专业领域展现出令人惊叹的实战能力。
愚公搬代码
2025/03/16
1260
DeepSeek横空出世,真的拯救了算力焦虑吗?
作者:watermelo37 涉及领域:Vue、SpingBoot、Docker、LLM、python等 --------------------------------------------------------------------- 温柔地对待温柔的人,包容的三观就是最大的温柔。 ---------------------------------------------------------------------
watermelo37
2025/02/19
1250
DeepSeek横空出世,真的拯救了算力焦虑吗?
DeepSeek的出现,让我们重新审视了仿真软件
近年来,大语言模型(LLM)技术的蓬勃发展,让人工智能在诸多领域得到广泛应用。然而,高昂的训练成本和资源门槛,让不少中小型企业和科研机构难以涉足这一前沿领域。
思茂信息
2025/02/11
1690
Deepseek 与 ChatGPT:AI 浪潮中的双子星较量
Deepseek 采用的混合专家模型(MoE)是其技术亮点之一。从原理上来说,MoE 将一个大的语言模型拆分成多个较小的专家模型,每个专家模型专注于处理特定类型的任务。当面对用户的问题时,模型会根据问题的特征,动态地将任务分配给最合适的专家模型。这就好比一个大型企业,不同的部门负责不同的业务板块,遇到业务时,能迅速找到对应的专业团队来处理,大大提高了处理效率。
喜欢做梦
2025/02/21
2490
Deepseek 与 ChatGPT:AI 浪潮中的双子星较量
DeepResearch产品形态与发展研究报告
DeepResearch是一种AI驱动的智能研究工具,旨在解决复杂、知识密集型的研究任务。其核心功能包括:
@小森
2025/03/07
6181
AI时代的教育之问Ⅵ:应用生态
教育系统作为社会系统的一个关键子系统,在人工智能技术的驱动下,教育现象变得日益错综复杂,教育动因更加难以解析,教育风险也愈发难以预见。为了有效应对人工智能技术对教育系统的挑战与机遇,必须进行跨学科、跨圈层和跨领域的深入对话。
小腾资讯君
2025/04/10
1880
服软了?爆料英伟达联手DeepSeek推出NIM服务
北京时间1月31日,英伟达正式宣布在其官网上发布了基于DeepSeek R1 671b模型的“英伟达NIM微服务”预览版,并高调宣称DeepSeek-R1的推理能力处于“最先进”水平。
用户11203141
2025/03/06
720
服软了?爆料英伟达联手DeepSeek推出NIM服务
DeepSeek驱动行业智变提速,腾讯云汇聚大咖共话进阶之路
DeepSeek 以迅雷之势席卷全球,并以实战为突破点,推动 AI 技术在实际应用中的深刻变革。我们站在这个新起点上,远眺 AI 技术落地最汹涌的浪潮。各行各业正积极探索如何利用 DeepSeek 等前沿 AI 技术实现业务创新。在众多 AI 技术和应用中,找到那些真正具有颠覆性和引领性的方向,并实现切实可行的落地应用,已成为企业当前关键的战略思考。
TVP官方团队
2025/04/04
1260
DeepSeek驱动行业智变提速,腾讯云汇聚大咖共话进阶之路
该我来蹭DeepSeek的热度了
但大家可能不知道的是DeepSeek在全球各个国家的关注热度都是怎样的,那么下面我们来看一组数据:截止到2025年2月16日,在Google上对比AI、ChatGPT、LLama和DeepSeek,这几个关键词的搜索趋势(也可以叫搜索热度),先看下国内的吧:
闫同学
2025/02/18
2290
该我来蹭DeepSeek的热度了
当我用DeepSeek预测AI for BioScience未来3-5年发展趋势
预测未来3-5年AI在生物科学(AI for BioScience)的发展趋势,可以从技术突破、跨学科融合、数据驱动创新以及伦理监管等多个维度进行分析。以下是一些关键趋势的展望:
实验盒
2025/02/05
2410
当我用DeepSeek预测AI for BioScience未来3-5年发展趋势
鏖战企业级市场,AI Agent如何重塑智能商业未来?
暂且不论Manus自身性能究竟达到何种高度,其引发的从“被动应答”到“主动执行”的颠覆性范式跃迁,正以前所未有的态势,不断突破繁杂业务流程场景下智能化的边界,为企业级市场带来无限可能。
科技云报道
2025/04/10
1450
鏖战企业级市场,AI Agent如何重塑智能商业未来?
DeepSeek引领端侧AI革命,边缘智能重构AI价值金字塔
2025年的AI战场,硝烟早已从“参数军备竞赛”转向更隐秘的角落。随着DeepSeek等新兴小模型的突破,一场以边缘侧智能为核心的变革正悄然重塑行业格局。算力下沉、场景驱动,AI正从实验室走向真实世界,以更低成本、更高效率重塑产业智能化。
CoovallyAIHub
2025/03/11
2110
DeepSeek引领端侧AI革命,边缘智能重构AI价值金字塔
DeepSeek大幅度降低大模型门槛,利好于Agents和应用落地
在人工智能领域,每一次技术突破都可能带来全新的可能性。近日,由DeepSeek推出的R1模型横空出世,以惊人的性价比和技术革新引发了全球瞩目。不仅硅谷风投教父Marc Andreessen将其称为“AI的斯普特尼克时刻”,甚至美国总统特朗普也公开表示,DeepSeek是对美国科技界的“警钟”。
张善友
2025/02/03
6490
DeepSeek大幅度降低大模型门槛,利好于Agents和应用落地
【DeepSeek】深度求索:DeepSeek的AI技术革新与行业突破
近年来,人工智能领域呈现出前所未有的发展速度,以深度学习为代表的技术不断突破行业边界。在这一浪潮中,中国科技公司深度求索(DeepSeek)凭借其独特的创新能力和技术积累,逐渐成为全球AI领域的重要参与者。本文将从DeepSeek的核心技术体系、行业应用场景、技术优势及未来发展方向等方面,深入探讨其在AI领域的突破性贡献。
远方2.0
2025/03/16
4870
【DeepSeek】深度求索:DeepSeek的AI技术革新与行业突破
推荐阅读
相关推荐
DeepSeek这么火,到底做对了什么?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验