C++是一种面向对象的编程语言,支持许多设计模式。以下是几种常见的设计模式:
1.单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点
来访问该实例。
2.工厂模式(Factory Pattern):定义一个接口以创建对象,但是让子类决定实例化
哪个类。工厂方法让类把实例化推迟到子类。
3.观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者
对象同时监听某一个主题对象,当主题对象状态发生改变时,它的所有依赖者(观察者)
都会收到通知并自动更新。
4.适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另一个接口。
适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。
5.模板方法模式(Template Method Pattern):定义一个操作中的算法的骨架,
将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下重新
定义算法的某些步骤。
6.策略模式(Strategy Pattern):定义一系列的算法,把它们封装起来,并且
使它们可以相互替换。策略模式使得算法可以独立于使用它的客户而变化。
7.命令模式(Command Pattern):将请求封装成对象,从而使你可以用不同的
请求对客户进行参数化。命令模式也支持撤销操作。
以上只是举几个例子,实际上C++中还有很多其他的设计模式,每种设计模式都有不同的
用途和优缺点,具体应该根据实际情况选择使用。
C++ 工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们创建对象而不会将创建逻辑暴露给客户端。我们创建一个工厂类,该类根据传递
给它的参数来创建对象。客户端不知道对象的实际创建过程。工厂模式的目的是将创建对象的代码集中
在一起,这样可以更轻松地管理和修改代码。
通过使用工厂模式,我们将对象的创建和使用分离开来,从而使代码更加清晰、灵活和易于维护。如果
我们需要添加新的产品类,只需要修改工厂类即可,客户端不需要进行任何修改
以下是一个简单的示例,展示了如何在C++中实现工厂模式:
#include <iostream>
using namespace std;
// 基类
class Product {
public:
virtual void show() = 0;
};
// 具体产品类 A
class ConcreteProductA : public Product {
public:
void show() {
cout << "This is ConcreteProductA." << endl;
}
};
// 具体产品类 B
class ConcreteProductB : public Product {
public:
void show() {
cout << "This is ConcreteProductB." << endl;
}
};
// 工厂类
class Factory {
public:
Product* createProduct(char type) {
if (type == 'A') {
return new ConcreteProductA();
}
else if (type == 'B') {
return new ConcreteProductB();
}
else {
return NULL;
}
}
};
int main() {
Factory* factory = new Factory();
Product* productA = factory->createProduct('A');
productA->show();
Product* productB = factory->createProduct('B');
productB->show();
delete factory;
delete productA;
delete productB;
return 0;
}
在这个示例中,我们定义了一个Product基类,以及两个具体产品类ConcreteProductA和ConcreteProductB。
我们还定义了一个Factory工厂类,该类具有createProduct方法,该方法根据传入的参数('A'或'B')来创建相应的产品实例。
在main函数中,我们首先创建一个工厂实例,然后使用工厂实例来创建两个产品实例。最后,我们在不再需要它们时删除了
所有实例。
通过使用工厂模式,我们可以将产品实例的创建逻辑从客户端代码中抽离出来,从而使得客户端代码更加简洁和易于维护。
以下是一个使用工厂模式的游戏开发例子。假设我们正在开发一个 RPG 游戏,游戏中有不同的敌人类型,每个敌人都有不同的攻击和
防御属性。我们可以使用工厂模式来创建不同类型的敌人。
首先是抽象基类 Enemy:
class Enemy {
public:
virtual ~Enemy() {}
virtual std::string getName() const = 0;
virtual int getAttack() const = 0;
virtual int getDefense() const = 0;
};
然后是派生类 Goblin 和 Orc:
class Goblin : public Enemy {
public:
std::string getName() const {
return "Goblin";
}
int getAttack() const {
return 20;
}
int getDefense() const {
return 10;
}
};
class Orc : public Enemy {
public:
std::string getName() const {
return "Orc";
}
int getAttack() const {
return 50;
}
int getDefense() const {
return 30;
}
};
接下来是工厂类 EnemyFactory:
class EnemyFactory {
public:
enum class Type {
Goblin,
Orc
};
std::unique_ptr<Enemy> createEnemy(Type type) const {
switch (type) {
case Type::Goblin:
return std::make_unique<Goblin>();
case Type::Orc:
return std::make_unique<Orc>();
default:
throw std::invalid_argument("Invalid enemy type");
}
}
};
最后,我们可以使用 EnemyFactory 来创建不同类型的敌人:
int main() {
EnemyFactory factory;
std::unique_ptr<Enemy> goblin = factory.createEnemy(EnemyFactory::Type::Goblin);
std::unique_ptr<Enemy> orc = factory.createEnemy(EnemyFactory::Type::Orc);
std::cout << goblin->getName() << " has attack " << goblin->getAttack() << " and defense " << goblin->getDefense() << std::endl;
std::cout << orc->getName() << " has attack " << orc->getAttack() << " and defense " << orc->getDefense() << std::endl;
return 0;
}
在这个例子中,我们使用了一个枚举类型 EnemyFactory::Type 来表示不同类型的敌人
。EnemyFactory 类的 createEnemy() 函数根据传入的类型参数来创建对应的敌人对象,
然后返回一个指向该对象的智能指针。我们还使用了 std::unique_ptr 智能指针来管理
敌人对象的生命周期。
这个例子展示了如何使用工厂模式来创建不同类型的敌人,这个模式可以帮助我们简化
代码,提高代码的可维护性和可扩展性。在游戏开发中,工厂模式可以被用来创建不同
类型的角色、武器、装备、道具等等。
在这个例子中,unique_ptr<Enemy> 用来管理 Enemy 对象的生命周期。由于我们使用了
工厂模式创建敌人对象,这些对象是动态分配的,需要在适当的时候释放它们的内存。
unique_ptr 可以确保当 Enemy 对象不再被使用时,它们的内存会被自动释放,避免了
内存泄漏的问题。
需要注意的是,由于 unique_ptr 是独占所有权的指针,不能被拷贝或移动,因此我们
需要使用 std::move() 函数将它的所有权转移给另一个 unique_ptr 对象,或者使用
std::make_unique() 函数来创建一个新的 unique_ptr 对象。在这个例子中,我们使用了
std::make_unique() 函数来创建 unique_ptr<Enemy> 对象。
make_unique<Goblin>() 是 C++14 标准中引入的用来创建动态分配对象的工厂函数。
它的返回值是一个 std::unique_ptr 智能指针,用于管理指向动态分配对象的内存。
在这个例子中,make_unique<Goblin>() 用来创建一个 Goblin 对象,并返回一个
unique_ptr<Goblin> 智能指针。由于使用了 make_unique 工厂函数,我们不需要
手动分配内存,也不需要手动释放内存,这些工作都由 unique_ptr 来处理。同时,
使用 make_unique 工厂函数还能提高代码的可读性和可维护性。
需要注意的是,make_unique 只能用来创建动态分配对象,不能用来创建静态对象。
而且,make_unique 的返回值是一个独占所有权的智能指针,不能被拷贝或移动。
如果需要共享所有权的指针,应该使用 std::shared_ptr 智能指针。
C++设计模式工厂模式在图像处理中的实例:
#include <iostream>
#include <memory>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
// 定义图像处理接口
class ImageProcessor {
public:
virtual void process(const Mat& input, Mat& output) = 0;
};
// 定义不同类型的图像处理类
class GaussianBlurProcessor : public ImageProcessor {
public:
virtual void process(const Mat& input, Mat& output) {
GaussianBlur(input, output, Size(5, 5), 0, 0);
}
};
class MedianBlurProcessor : public ImageProcessor {
public:
virtual void process(const Mat& input, Mat& output) {
medianBlur(input, output, 5);
}
};
class BilateralFilterProcessor : public ImageProcessor {
public:
virtual void process(const Mat& input, Mat& output) {
bilateralFilter(input, output, 9, 75, 75);
}
};
// 定义图像处理工厂类
class ImageProcessorFactory {
public:
enum ProcessorType {
GAUSSIAN_BLUR, //高斯滤波
MEDIAN_BLUR, //中值滤波
BILATERAL_FILTER //双边滤波
};
static std::shared_ptr<ImageProcessor> createProcessor(ProcessorType type) {
switch (type) {
case GAUSSIAN_BLUR:
return std::make_shared<GaussianBlurProcessor>();
case MEDIAN_BLUR:
return std::make_shared<MedianBlurProcessor>();
case BILATERAL_FILTER:
return std::make_shared<BilateralFilterProcessor>();
default:
throw std::invalid_argument("Invalid processor type.");
}
}
};
int main() {
Mat input = imread("input.jpg");
Mat output;
// 使用工厂模式创建不同类型的图像处理对象
Auto gaussianBlurProcessor = ImageProcessorFactory::createProcessor(ImageProcessorFactory::GAUSSIAN_BLUR);
Auto medianBlurProcessor = ImageProcessorFactory::createProcessor(ImageProcessorFactory::MEDIAN_BLUR);
auto bilateralFilterProcessor = ImageProcessorFactory::createProcessor(ImageProcessorFactory::BILATERAL_FILTER);
// 使用不同的图像处理对象处理输入图像
gaussianBlurProcessor->process(input, output);
imwrite("gaussian_blur.jpg", output);
medianBlurProcessor->process(input, output);
imwrite("median_blur.jpg", output);
bilateralFilterProcessor->process(input, output);
imwrite("bilateral_filter.jpg", output);
return 0;
}
"auto"是C++11引入的关键字,表示根据变量初始化的值自动推导出其类型。在这个代码中,
它意味着编译器会自动推导出gaussianBlurProcessor变量的类型,而不需要程序员
显式地指定它的类型。因此,该变量的类型将是根据ImageProcessorFactory::
createProcessor方法返回的对象类型自动确定的。
单例模式是一种常用的设计模式,它的主要作用是确保一个类只有一个实例,并且提供一个全局访问点。
下面是一个C++实现的单例模式示例代码:
#include <iostream>
class Singleton
{
public:
// 获取实例的静态方法
static Singleton* getInstance()
{
if (instance == nullptr) // 如果实例不存在,则创建一个新实例
{
instance = new Singleton();
}
return instance;
}
// 输出信息的方法
void showMessage()
{
std::cout << "Hello, World!" << std::endl;
}
private:
// 禁止外部实例化
Singleton() {}
// 静态成员变量,用于存储唯一实例的指针
static Singleton* instance;
};
// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;
int main()
{
// 通过静态方法获取实例
Singleton* instance1 = Singleton::getInstance();
Singleton* instance2 = Singleton::getInstance();
// 输出实例地址,可以看出是同一个实例
std::cout << "Instance 1 address: " << instance1 << std::endl;
std::cout << "Instance 2 address: " << instance2 << std::endl;
// 调用实例的方法
instance1->showMessage();
return 0;
}
在上面的代码中,Singleton类中声明了一个静态成员变量instance,用于存储唯一实例
的指针。getInstance()方法则用于获取这个实例的指针,如果实例不存在,则创建一个
新实例。构造函数Singleton()被设置为private,这样就禁止了外部实例化,从而确保
只有一个实例存在。
在main()函数中,我们通过Singleton::getInstance()静态方法获取了两个实例的指针,
然后输出了它们的地址,可以看出它们是同一个实例。最后,我们调用了实例的
showMessage()方法,输出了“Hello, World!”这个信息。
这个示例代码比较简单,但是可以很好地演示单例模式的实现方式。需要注意的是,
单例模式可能会带来一些问题,比如可能会影响程序的扩展性和测试性,因此在使用时需要慎重考虑。
在单例模式中,需要确保只有一个实例存在,而且这个实例需要在程序运行期间始终存在,不能被销毁
或重新创建。为了实现这个要求,可以将实例存储为一个静态成员变量。
静态成员变量是属于类的,而不是属于类的实例的。因此,无论创建多少个类的实例,静态成员变量都
只有一个,它们的值在所有实例中都是相同的。这正好符合单例模式的要求,即只有一个实例存在。
另外,静态成员变量还有一个优点,就是可以在类的静态方法中访问它们,而不需要通过类的实例。
这样可以简化代码,使代码更加清晰。
需要注意的是,静态成员变量的生命周期与程序的生命周期相同,它们只会在程序结束时被销毁。因此,
如果不需要使用单例实例时,可以将其销毁以释放内存。同时,在多线程环境中,需要考虑使用线程安全
的方式来访问静态成员变量。
假设我们有一个 Logger 类,用于记录系统日志。由于日志文件的创建和写入操作比较耗时,我们希望整个程序中
只有一个 Logger 实例,以避免重复创建和写入日志文件,从而提高程序的性能和可维护性。这时可以使用单例模式来实现。
以下是一个简单的 Logger 单例模式实例代码:
#include <iostream>
#include <fstream>
#include <string>
class Logger {
public:
static Logger& getInstance() {
static Logger instance; // 静态局部变量
return instance;
}
void log(const std::string& message) {
file << message << std::endl;
}
private:
Logger() {
file.open("log.txt", std::ios::out | std::ios::app);
}
~Logger() {
file.close();
}
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
std::ofstream file; // 日志文件流
};
int main() {
Logger& logger = Logger::getInstance();
logger.log("Start logging.");
logger.log("Logging message 1.");
logger.log("Logging message 2.");
logger.log("Logging message 3.");
logger.log("Finish logging.");
return 0;
}
在上面的代码中,我们定义了 Logger 类,它有一个静态方法 getInstance(),用于获取 Logger
的唯一实例。这个实例也是通过静态局部变量实现的,保证了实例的唯一性。同时,Logger 类也
禁止了拷贝构造函数和赋值运算符的调用,以避免在程序中出现多个 Logger 实例。
在 Logger 类中,我们定义了 log() 方法,用于向日志文件中写入日志信息。在构造函数中,
我们打开了日志文件流,并在析构函数中关闭了日志文件流。
在 main() 函数中,我们通过 Logger::getInstance() 获取了 Logger 的唯一实例,并调用
了它的 log() 方法来写入日志信息。这样,整个程序中只有一个 Logger 实例,可以有效避免
重复创建和写入日志文件的操作,从而提高程序的性能和可维护性。
假设我们有一个 Config 类,用于读取和保存系统配置信息。由于配置信息的读取和保存操作比较耗时,我们希望整个
程序中只有一个 Config 实例,以避免重复读取和保存配置信息,从而提高程序的性能和可维护性。这时可以使用单例
模式来实现。
以下是一个简单的 Config 单例模式实例代码:
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
class Config {
public:
static Config& getInstance() {
static Config instance; // 静态局部变量
return instance;
}
void load(const std::string& filename) {
std::ifstream file(filename);
if (file.is_open()) {
std::string line;
while (std::getline(file, line)) {
size_t pos = line.find('=');
if (pos != std::string::npos) {
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
data[key] = value;
}
}
file.close();
} else {
std::cerr << "Failed to open file " << filename << std::endl;
}
}
void save(const std::string& filename) {
std::ofstream file(filename);
if (file.is_open()) {
for (const auto& [key, value] : data) {
file << key << "=" << value << std::endl;
}
file.close();
} else {
std::cerr << "Failed to open file " << filename << std::endl;
}
}
std::string get(const std::string& key) const {
auto it = data.find(key);
return (it != data.end()) ? it->second : "";
}
private:
Config() {}
Config(const Config&) = delete;
Config& operator=(const Config&) = delete;
std::unordered_map<std::string, std::string> data; // 配置信息
};
int main() {
Config& config = Config::getInstance();
config.load("config.txt");
std::cout << "Config data: " << std::endl;
std::cout << "host = " << config.get("host") << std::endl;
std::cout << "port = " << config.get("port") << std::endl;
config.save("config.txt");
return 0;
}
在上面的代码中,我们定义了 Config 类,它有一个静态方法 getInstance(),用于获取 Config 的
唯一实例。这个实例也是通过静态局部变量实现的,保证了实例的唯一性。同时,Config 类也禁止了
拷贝构造函数和赋值运算符的调用,以避免在程序中出现多个 Config 实例。
在 Config 类中,我们定义了 load() 方法和 save() 方法,分别用于从文件中读取和保存配置
信息。在 get() 方法中,我们通过关键字查找来获取配置信息。这些操作都是基于 Config 类的
实例进行的,保证了整个程序中只有一个 Config 实例,避免了重复读取和保存配置信息的操作。
c++中观察者模式详解和实例
观察者模式(Observer Pattern)是一种行为型模式,它定义了对象之间一对多的依赖关系,当一个对象的状态
发生改变时,它的所有依赖者都会收到通知并自动更新。在C++中,观察者模式可以通过使用标准库中的observable
和observer来实现。
实现观察者模式需要有两个角色:
1.被观察者(Observable):它定义了一个抽象接口,用于添加、删除和通知观察者。
2.观察者(Observer):它定义了一个抽象接口,用于接收被观察者的通知。
下面是一个简单的实例,演示如何在C++中实现观察者模式:
#include <iostream>
#include <string>
#include <vector>
// 观察者抽象接口
class Observer {
public:
virtual void update(const std::string& message) = 0;
};
// 被观察者抽象接口
class Observable {
public:
virtual void addObserver(Observer* observer) = 0;
virtual void removeObserver(Observer* observer) = 0;
virtual void notifyObservers(const std::string& message) = 0;
};
// 被观察者实现类
class Teacher : public Observable {
public:
void addObserver(Observer* observer) override {
m_observers.push_back(observer);
}
void removeObserver(Observer* observer) override {
m_observers.erase(std::remove(m_observers.begin(),m_observers.end(),observer), m_observers.end());
}
void notifyObservers(const std::string& message) override {
for (auto observer : m_observers) {
observer->update(message);
}
}
private:
std::vector<Observer*> m_observers;
};
// 观察者实现类
class Student : public Observer {
public:
explicit Student(const std::string& name) : m_name(name) {}
void update(const std::string& message) override {
std::cout << "Student " << m_name << " received message: " << message << std::endl;
}
private:
std::string m_name;
};
int main() {
// 创建被观察者和观察者
Teacher teacher;
Student alice("Alice");
Student bob("Bob");
// 注册观察者
teacher.addObserver(&alice);
teacher.addObserver(&bob);
// 发送通知
teacher.notifyObservers("Class is canceled.");
// 注销观察者
teacher.removeObserver(&bob);
// 发送通知
teacher.notifyObservers("Class is rescheduled.");
return 0;
}
在这个例子中,我们创建了一个Teacher类作为被观察者,它维护了一个Observer的向量m_observers,
用于存储所有观察者。Teacher类实现了Observable接口,并实现了添加、删除和通知观察者的方法。
我们还创建了一个Student类作为观察者,它实现了Observer接口,并定义了一个update()方法用于
接收。
以下是一个更具体的 C++ 观察者模式的示例代码,它模拟了一个温度传感器和多个显示器的场景。
#include <iostream>
#include <vector>
using namespace std;
class Observer
{
public:
virtual void update(float temperature) = 0;
};
class Subject
{
public:
void attach(Observer* observer)
{
observers.push_back(observer);
}
void detach(Observer* observer)
{
for (auto iter = observers.begin(); iter != observers.end(); ++iter)
{
if (*iter == observer)
{
observers.erase(iter);
break;
}
}
}
void notify(float temperature)
{
for (auto observer : observers)
{
observer->update(temperature);
}
}
private:
vector<Observer*> observers;
};
class TemperatureSensor : public Subject
{
public:
void setTemperature(float temperature)
{
this->temperature = temperature;
notify(temperature);
}
private:
float temperature;
};
class FahrenheitDisplay : public Observer
{
public:
FahrenheitDisplay(Subject* subject) : subject(subject)
{
subject->attach(this);
}
void update(float temperature) override
{
cout << "Fahrenheit: " << temperature * 9 / 5 + 32 << endl;
}
private:
Subject* subject;
};
class CelsiusDisplay : public Observer
{
public:
CelsiusDisplay(Subject* subject) : subject(subject)
{
subject->attach(this);
}
void update(float temperature) override
{
cout << "Celsius: " << temperature << endl;
}
private:
Subject* subject;
};
int main()
{
TemperatureSensor sensor;
FahrenheitDisplay fahrenheitDisplay(&sensor);
CelsiusDisplay celsiusDisplay(&sensor);
sensor.setTemperature(20);
sensor.setTemperature(25);
return 0;
}
在上面的代码中,我们定义了 Observer 和 Subject 两个抽象类,其中 Observer 定义了一个抽象
的 update 方法,而 Subject 则定义了三个方法,分别是 attach、detach 和 notify。
接着,我们定义了一个具体的主题类 TemperatureSensor,它继承自 Subject 类,并实现了一个
setTemperature 方法,当温度发生变化时,它会调用 notify 方法发送通知给所有观察者。
然后,我们定义了两个具体的观察者类 FahrenheitDisplay 和 CelsiusDisplay,它们分别继承自
Observer 类,并在构造函数中将自己注册到指定的主题中,以便能够接收到主题的通知。在实现的
update 方法中,它们分别根据温度值计算出华氏温度和摄氏温度,并输出到控制台上。
最后,在 main 函数中,我们创建了一个 TemperatureSensor 对象和两个显示器对象,并将它们
注册到主题中。然后我们分别设置两个不同的温度值,观察者们都能够接收到通知并作出响应。