C++-第二十一章:特殊类设计
目录
第一节:特殊类
1-1.不能被拷贝的类
1-2.只能在堆上构造的类
1-3.只能在栈上构造的类
1-4.只能构造一个对象的类
第二节:工厂模式
下期预告:
第一节:特殊类
1-1.不能被拷贝的类
不能被拷贝的类有线程类、std::unique_ptr、流插入和流提取。
不能被拷贝是因为它们自己的唯一性,例如:每个线程都有唯一id;还有自己的功能限制,例如:std::unique_str就是为了解决多次释放内存才特地这样设计的;最后是必要性,流插入、流提取就没有必要就拷贝多份。
1-2.只能在堆上构造的类
实现方法:
1、在private下实现一个构造函数,这样各种默认构造函数就不会生成了(默认构造、默认拷贝构造、默认移动构造、默认拷贝赋值、默认移动赋值)
2、在public:下实现一个静态函数,这个函数的功能是在堆上创建一个类对象,并把这个对象返回。
完成以上两步之后,就只能通过类域中的静态函数获取类了,而该函数是从堆上申请的空间:
class HeapOnly { private: // 第一步 HeapOnly() {} public: // 第二步 static HeapOnly* GetClass() { return new HeapOnly; } };
如果想传入参数用这些参数来初始化类,那么在private下实现对应参数的构造函数并在private重载一个对应参数的GetClass即可:
class HeapOnly { private: // 第一步 HeapOnly() {} // 实现传入参数的类 HeapOnly(int num) { GetClass(num); } public: // 第二步 static HeapOnly* GetClass() { return new HeapOnly; } // 重载 static HeapOnly* GetClass(int num) { return new HeapOnly(num); } };
1-3.只能在栈上构造的类
只能在栈上构造的类需要重载new和delete:
class StackOnly { public: // 构造函数 StackOnly() {} private: // 禁用new操作符 void* operator new(size_t) = delete; // 禁用new[]操作符 void* operator new[](size_t) = delete; // 禁用delete操作符 void operator delete(void*) = delete; // 禁用delete[]操作符 void operator delete[](void*) = delete; };
1-4.只能构造一个对象的类
只能构造一个对象这种模式叫做单例模式,单例模式的实现方式如下:
(1)在private下显式创建构造函数,防止外部调用构造函数
(2)在private下声明一个该类的static对象
(3)在类外部定义(1)声明的对象
class Singleton { private: Singleton() {} // 第一步 static Singleton _oneClass; // 第二步 }; // 第三步 Singleton Singleton::_oneClass;
这样的话只能使用Singleton只有 _oneClass 这一个对象了。
这种实现单例模式的方式是饿汉模式,即在main函数之前就将单例的对象生成,这种方式会有两个问题:
(1)饿汉在main函数之前创建对象,会减缓进入main函数的速度
(2)如果饿汉之间有依赖关系,还需要注意饿汉的初始化顺序
为了解决上述两个问题,于是又有了懒汉模式。
懒汉模式的特征是在第一次使用单例对象才创建对象,这需要在单例类的public下提供一个静态接口,并删除类外的单例对象定义:
class Singleton { private: Singleton() {} // 第一步 static Singleton _oneClass; // 第二步 public: // 懒汉模式 static Singleton* GetOneClass() { static Singleton _oneClass; // 定义 return &_oneClass; } };
因为 _oneClass 的定义用static进行了修饰,那么只有第一次调用这个接口时才会初始化对象,之后的调用只是得到了单例对象的指针。
通过这个指针就可以使用_oneClass了。
第二节:工厂模式
工厂模式就是使用类创建一个"工厂",给这个工厂不同的参数,工厂就会返回一个对应对象。
假如有一个游戏,游戏中包含了不同的怪物,那么我们就可以通过工厂生成多个不同类型的怪物:
#include <iostream> #include <memory> #include <string> // 抽象基类:怪物 class Monster { public: virtual ~Monster() {} // 攻击虚函数 virtual void attack() const = 0; }; // 具体产品类:哥布林 class Goblin : public Monster { public: void attack() const override { std::cout << "哥布林使用匕首攻击" << std::endl; } }; // 具体产品类:喷火龙 class Dragon : public Monster { public: void attack() const override { std::cout << "喷火龙使用火球术" << std::endl; } }; // 工厂类 class MonsterFactory { public: static std::unique_ptr<Monster> createMonster(const std::string& type) { if (type == "哥布林") { // 构造一个哥布林 return std::make_unique<Goblin>(); } else if (type == "喷火龙") { // 构造一个喷火龙 return std::make_unique<Dragon>(); } else { return nullptr; } } }; int main() { // 使用工厂创建不同类型的怪物 std::unique_ptr<Monster> goblin = MonsterFactory::createMonster("哥布林"); std::unique_ptr<Monster> dragon = MonsterFactory::createMonster("喷火龙"); if (goblin) { goblin->attack(); } if (dragon) { dragon->attack(); } return 0; }
当然不止 attack 函数,还可以设置其他函数,例如行动力函数,它决定怪物的移动方式等。
下期预告:
没有下期了,因为这是最后一章了。