gpt4 book ai didi

c++ - 观察者常量正确性

转载 作者:行者123 更新时间:2023-11-30 03:40:54 25 4
gpt4 key购买 nike

我正在尝试在我的项目中实现观察者模式。

想象一个简单的类方法

const Buffer * data() const
{
if (m_data)
return m_data;

// read some data from input
m_data = fstream.read(1000);

// subscribe to our buffer
m_data->Subscribe(this);

return m_data;
}

该方法用于读取输入数据,但操作比较耗时,因此延迟。

Class Buffer 是 std::vector 之上的简单包装器,它在数据被更改时通知观察者。

当 Buffer 数据发生变化时,需要通知包含类。但是,由于此方法被标记为 const,我无法订阅缓冲区。

我能够找出 3 个解决方案:

1. 摒弃常量

    // subscribe to our buffer
m_data->Subscribe(const_cast<Object*>(this));

我不确定这是否正确,但它有效。

2.改变通知方法和观察者的const-ness

    vector<const IModifyObserver*> m_observers;
void Subscribe(const IModifyObserver* observer);
void Unsubscribe(const IModifyObserver* observer)
virtual void ObserveeChanged(IModifyObservable*) const override
{
m_dirty = true;
}

这个有一个缺点,如果我需要更改属性,它们都必须是可变的并且我调用的所有函数都必须是常量,这也没有任何意义。

3. 从所有地方删除 const

    Buffer * data();
bool Equals(Object& other);
Buffer* m_data;

这很可能意味着,我必须从整个解决方案中删除 const,因为我不能为两个不同的 const 对象事件调用 Equals。

如何正确解决这个问题?

完整代码:

#include <vector>

using namespace std;

class IModifyObservable;

// class for receiving changed notifications
class IModifyObserver
{
public:
virtual void ObserveeChanged(IModifyObservable* observee) = 0;
virtual ~IModifyObserver() = 0;
};

// class for producing changed notifications
class IModifyObservable
{
public:
// Add new subscriber to notify
void Subscribe(IModifyObserver* observer)
{
m_observers.push_back(observer);
}

// Remove existing subscriber
void Unsubscribe(IModifyObserver* observer)
{
for (auto it = m_observers.begin(); it != m_observers.end(); ++it) {
if (observer == *it) {
m_observers.erase(it);
break;
}
}
}

// Notify all subscribers
virtual void OnChanged()
{
auto size = m_observers.size();
for (decltype(size) i = 0; i < size; ++i) {
m_observers[i]->ObserveeChanged(this);
}
}

virtual ~IModifyObservable() = 0;

private:
vector<IModifyObserver*> m_observers;
};

IModifyObserver::~IModifyObserver() {}
IModifyObservable::~IModifyObservable() {}

// Example class implementing IModifyObservable
class Buffer : public IModifyObservable
{
private:
vector<char> m_data;
};

// Example class implementing IModifyObserver
class Object : public IModifyObserver
{
public:

// Both share implementation
//Buffer * data();
const Buffer * data() const
{
// Just read some data
//m_data = fstream.read(1000);

// Subscribe to our buffer
m_data->Subscribe(this);

return m_data;
}

virtual void ObserveeChanged(IModifyObservable*) override
{
m_dirty = true;
}

// This is just for example, why do I need const data method
bool Equals(const Object& other) const { return data() == other.data();
}

private:
mutable Buffer* m_data = new Buffer();
bool m_dirty;
};

int main()
{
Object obj1;
Object obj2;
auto data1 = obj1.data();
auto data2 = obj2.data();
bool equals = (obj1.Equals(obj2));
}

最佳答案

这里的障碍是你延迟阅读。如果没有这种优化,正确的方法是将常量和非常量方法分开:

const Buffer * data() const
{
return m_data;
}

void InitializeData()
{
// Just read some data
m_data = fstream.read(1000);

// Subscribe to our buffer
m_data->Subscribe(this);
}

然后按照你想要的方式优化它:

const Buffer * data() const
{
if(m_data == nullptr)
const_cast<Object*>(this)->InitializeData();

return m_data;
}

而且您不再需要可变的 m_data


顺便说一句。要使此延迟初始化工作,您应该使用 nullptr 初始化 m_data 成员。否则,该对象将在构造时创建,您的 if(m_data) 将始终为真。


更新

所以这是你问题的另一种解决方案

class Object : public IModifyObserver
{
public:

Object()
: m_data(nullptr)
, m_dataInitialized(false)
// ...
{
m_data = new Buffer(); // << Create buffer here
m_data->Subscribe(this); // << And subscribe right away
}

const Buffer * data() const
{
if(!m_dataInitialized) // << Initialize if needed
{
// Set data here
m_data->setData(fstream.read(1000)); // << Probably you want to suppress notifications here
m_dataInitialized = true;
}
return m_data;
}
// ...
private:
mutable Buffer* m_data;
mutable bool m_dataInitialized; // << Added another flag to see if data was initialized
// ...
};

关于c++ - 观察者常量正确性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37732541/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com