我正在使用现有的C库(我无法修改),其中某些结构具有必须通过特定的setter和getter访问的不透明字段,如下面的粗略示例(想象x
是私人的,即使它是用C)写的。
struct CObject {
int x;
};
void setCObjectX(CObject* o, int x) {
o->x = x;
}
int getCObjectX(CObject* o) {
return o->x;
}
我正在编写私有拥有这些类型结构的类,有点像包装器,虽然更复杂。我想以方便的方式公开相关领域。起初,我只是在必要时编写setter和getter。但是,我想到了其他的东西,我想知道这个方法是否有任何缺点。它使用函数指针(std::function
)来存储C setter-getter对,并将它们呈现为直接访问字段而不是函数。
这是我写的通用类,用于帮助定义这样的"假的"字段:
template<typename T>
struct IndirectField {
void operator=(const T& value) {
setter(value);
}
auto operator()() const -> T {
return *this;
}
operator T() const {
return getter();
}
std::function<void(const T&)> setter;
std::function<T()> getter;
};
通过在C ++类中定义实例并使用相应的C函数设置setter
和getter
来使用它:
IndirectField<int> x;
// ...
x.setter = [=](int x) {
setCObjectX(innerObject.get(), x);
};
x.getter = [=]() {
return getCObjectX(innerObject.get());
};
Here is a complete, working code for testing.
使用这种方法有什么缺点吗?它会导致最终的危险行为吗?
答案 0 :(得分:2)
我在您的解决方案中遇到的最大问题是std::function
个对象占用了IndirectField
内CPPObject
的每个实例内的空间,即使CObject
类型相同。 / p>
您可以通过将函数指针设置为模板参数来解决此问题:
template<typename T,typename R,void setter(R*,T),T getter(R*)>
struct IndirectField {
IndirectField(R *obj) : obj(obj) {
}
void operator=(const T& value) {
setter(obj, value);
}
auto operator()() const -> T {
return *this;
}
operator T() const {
return getter(obj);
}
private:
R *obj;
};
以下是如何使用此实现:
class CPPObject {
std::unique_ptr<CObject,decltype(&freeCObject)> obj;
public:
CPPObject()
: obj(createCObject(), freeCObject)
, x(obj.get())
, y(obj.get()) {
}
IndirectField<int,CObject,setCObjectX,getCObjectX> x;
IndirectField<double,CObject,setCObjectY,getCObjectY> y;
};
此方法为每std::function
个CObject*
指针交换两个IndirectField
个对象。不幸的是,存储此指针是必需的,因为您无法从模板内的上下文中获取它。
答案 1 :(得分:1)
使用这种方法有什么缺点吗?
您的代码中需要强调一些事项:
auto operator()()
,operator T()
仅限2)。它会导致最终的危险行为吗?
封装的破坏将导致x从任意数量的路线改变;并强迫其他事物知道它是如何存储在对象中的。这很糟糕。
创建IndirectField
意味着每个对象都必须以这种方式拥有getter和setter;这将是一场维护噩梦。
我觉得你真正想要的是:
struct xProvider {
virtual int getX() const = 0;
virtual void setX() = 0;
};
struct MyCObject : xProvider {
private:
CObject obj;
public:
int getX() const override {return obj.x;}
CObject& getRawObj() {return obj;}
// etc ...
}
然后你只需将参考/指针传递给xProvider。 这将消除对此外部C库的依赖;如果你认为合适,允许你用你自己的测试结构或一个全新的库替换它;无需使用它重写所有代码
答案 2 :(得分:0)
public
中的(发布时)所有字段均为protected
,因此客户端软件可以访问这些字段。我想让它们可以被派生类访问(如果您知道字段合同并且想要以明确定义的方式访问它,则不需要重新实现任何内容)它们是private
。如果您希望无人访问它们,请将它们标记为private
。
如果此类软件的作者不希望您触及这些字段,他会将其标记为set_myField()
,然后您将无所事事,但要适应这种行为。不这样做会给你带来不良后果。
假设您创建了一个使用<select>
方法修改的字段,该方法可以在您进行更改时随时调用侦听器列表。如果绕过方法访问功能,将绕过所有侦听器(其中许多来源不明),并且不会收到有关字段更改的通知。这在对象编程中非常常见,因此您必须遵守作者对您施加的规则。