![](/img/trans.png)
[英]C++ How to use same class and methods for reading/writing variable size data
[英]C++: How can I use different implementation of methods with the same data class?
背景:我所涉及的程序的各个模块处理的是对象的相同组合,这些对象以聚合结构分组在一起。 对对象的组合施加了众所周知的不变量,所有模块都充分尊重了这些不变量。 每个模块都是由专门的团队开发的,每个团队都需要使用其特定于域的自定义方法来处理对象的组合。
示例:为了给您一个切实的想法,请想象一个序列容器类。 容器的核心在所有用户中都是相同的:它由用于存储,大小/容量和分配器的数据成员组成。 但是方法集,合同和这些方法的主体可能相差很大。 一个模块可以实现std风格的操作,另一个模块可以将所有操作实现为nothrow方法,而另一个模块可以坚持使用其私有检查的迭代器; 一些对性能要求严格的模块要禁止所有复制操作,而另一个模块却只能复制。...这些要求在任何给定模块的每个特定领域中都是合理的。
推测:因此,不可能提供一个可以满足所有客户团队需求的单一非冗余方法集-有些团队的要求是相互排斥的。 只提供所有模块通常需要的那些方法是没有用的,因为唯一的共同部分可能是析构函数。 将所有方法的所有可能的实现放在一起也不是一件好事:可维护性和稳定性差,接口混乱而,肿,许多名称冲突,许多跨模块依赖性。
问题:要让多个独立的实现对同一组数据成员进行操作,我必须具有哪些选择?
我尝试过的事情:到目前为止,我所看到的解决方案并不十分理想,我对其中任何一个都不完全满意。 我将在答案中列出它们,三种方法一一列出。
对于我自己的问题,可能有一种不太理想的解决方案:
1.不用理会方法,而要拥有独立的功能。
struct CoreData
{
int m_x;
~CoreData();
};
void TeamA_init(CoreData& data);
void TeamA_push_back(CoreData& data, Whatever args);
Iter TeamA_begin(CoreData& data);
bool TeamB_init(CoreData& data, Other args);
bool TeamB_push_back(CoreData& data, Whatever args);
X* TeamB_begin(CoreData& data);
//--------------------- Usage:
void ServiceOfTeamA::CallServiceOfTeamB(ServiceOfTeamB* srv)
{
CoreData d;
TeamA_init(d);
srv->Process(&d);
TeamA_begin(d);
}
void ServiceOfTeamB::Process(CoreData* d)
{
TeamB_push_back(*d, 567);
}
-我对这种方法不满意的是语法不友好,没有RAII和所有数据成员都是公开的。 那是C,不是C ++。
+从好的方面来说,这种方法提供了无限的定制可能性。 为任务选择正确的功能没有任何限制。 没有内存开销,没有运行时开销(也就是说,从技术上讲,编译器具有与那些自由函数作为方法相同的内联和优化机会)。
对于我自己的问题,可能有一种不太理想的解决方案:
2.具有一组通过引用对核心数据的外部实例进行操作的类。
struct CoreData
{
int m_x;
~CoreData();
};
class TeamA
{
public:
// Allocates a new instance and owns it.
TeamA();
// Attaches an external instance without assuming ownership.
TeamA(CoreData& ext);
// Release the core, if must.
~TeamA();
void push_back(Whatever args);
Iter begin();
CoreData& GetCore();
private:
CoreData* m_core;
bool m_doIOwnThatCore;
};
class TeamB
{
public:
TeamB();
TeamB(CoreData& ext);
~TeamB();
int push_back(Whatever args);
CoreData& GetCore();
private:
CoreData* m_core;
bool m_doIOwnThatCore;
};
//--------------------- Usage:
void ServiceOfTeamA::CallServiceOfTeamB(ServiceOfTeamB* srv)
{
TeamA d;
srv->Process(d.GetCore());
d.begin();
}
void ServiceOfTeamB::Process(CoreData* core)
{
TeamB d(core);
d.push_back(567);
}
-我不喜欢这种方法,因为它在内存使用和性能方面略有悲观。 语法还使TeamA和TeamB类型的对象看起来像值,而实际上它们具有引用语义。
+好消息是,这种方法为调用提供了更好的C ++语法(但仍然有丑陋的GetCore()),并且符合RAII。
对于我自己的问题,可能有一种不太理想的解决方案:
3.将代码扔在事实上定义的reinterpret_cast行为上。
// This is a standard layout class.
// It is the only class with data members;
// derived classes never append new data members.
class CoreData
{
public:
// Could be either explicit conversion function
// or implicit conversion operator.
template <typename Final>
// requires <LayoutCompatibleWithCore Final>
Final& As()
{
return reinterpret_cast<Final&>(*this);
}
protected:
~CoreData();
int m_x;
};
// No extra data members appended. No extra invariants imposed.
// This class is also a standard layout type,
// fully layout-compatible with CoreData.
class TeamA : public CoreData
{
public:
void push_back(Whatever args);
Iter begin();
};
class TeamB : public CoreData
{
public:
bool push_back(Whatever args);
X* begin();
};
//--------------------- Usage:
void ServiceOfTeamA::CallServiceOfTeamB(ServiceOfTeamB* srv)
{
TeamA d;
srv->Process(&d);
d.begin();
}
void ServiceOfTeamB::Process(CoreData* core)
{
TeamB& d = core->As<TeamB>();
d.push_back(567);
}
-但是,这些技巧在标准中是非法的。 因此,我也必须拒绝这种方法。
+如果这是合法的,那么它将提供三种语言中最好的语法,该语法清楚地显示了参考语义,并且具有RAII并且没有悲观化。
PS这种方法的无效性使我失望。 布局兼容性的整个方面似乎都提供了在ABI兼容进程或共享组件之间传递数据的能力。 太糟糕了,它不允许在同一应用程序的各个部分之间传递数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.