我正在将结构转换为类,因此我可以为我的变量强制执行setter接口 但是,我不想更改读取变量的所有实例。 所以我转换了这个:
struct foo_t {
int x;
float y;
};
到此:
class foo_t {
int _x;
float _y;
public:
foot_t() : x(_x), y(_y) { set(0, 0.0); }
const int &x;
const float &y;
set(int x, float y) { _x = x; _y = y; }
};
我对此很感兴趣,因为它似乎模仿了C#的公共只读属性的概念 编译很好,我还没有看到任何问题。
除了在构造函数中关联const引用的样板外,这个方法的缺点是什么? 有什么奇怪的别名问题吗? 为什么我以前没见过这个成语?
答案 0 :(得分:11)
一个问题是你的类不再可复制或可分配,因此不能存储在像vector这样的C ++容器中。另一个是经验丰富的C ++程序员维护你的代码会看到它并惊呼“WTF !!”非常响亮,这绝不是一件好事。
答案 1 :(得分:5)
由于您公开了对foo_t
内部数据的引用,因此存在一个别名问题,foo_t
对象外部的代码可能会保留对其数据的引用。对象的一生。考虑:
foo_t* f = new foo_t();
const int& x2 = f->x;
delete f;
std::cout << x2; // Undefined behavior; x2 refers into a foo_t object that was deleted
或者,甚至更简单:
const int& x2 = foo_t().x;
std::cout << x2; // Undefined behvior; x2 refers into a foo_t object that no longer exists
这些并不是特别现实的例子,但只要对象公开或返回对其数据(公共或私有)的引用,这就是一个潜在的问题。当然,尽可能在其生命周期之后继续引用foo_t
对象本身,但这可能更难以错过或偶然发生。
并不是说这是反对你正在做的事情的论据。事实上,之前我曾经使用过这种模式(出于不同的原因),除了缺乏封装之外,我认为它本身并没有任何错误,你似乎已经认识到了这一点。上述问题只是需要注意的事项。
答案 2 :(得分:2)
你也可以这样做,适用于内置类型: (很抱歉,如果此代码段包含错误,但您明白了这一点)
template <typename T, typename F>
class read_only{
typedef read_only<T, F> my_type;
friend F;
public:
operator T() const {return mVal;}
private:
my_type operator=(const T& val) {mVal = val; return *this;}
T mVal;
};
class MyClass {
public:
read_only <int, MyClass> mInt;
void MyFunc() {
mInt = 7; //Works
}
};
AnyFunction(){
MyClass myClass;
int x = myClass.mVal; // Works (okay it hasnt been initalized yet so you might get a warning =)
myClass.mVal = 7; // Error
}
答案 3 :(得分:0)
您的方法不灵活。如果您为每个变量都有getter / setter
,则表示如果您向班级添加内容,则无需重写set
方法。
这不好,因为你不能拥有const
和non-const
getters (很少使用,但有时可能会有用)。
您无法复制引用,因此,您的类将变为不可复制。
另外,在你的类中引用初始化意味着额外的内存,如果我们正在讨论,例如,顶点类(虽然我认为它实际上不应该是一个类),这可能成为一场灾难。
[以下所有内容完全是主观的]
在我看来,getter和setter的目的不是简单的修改,而是封装一系列导致值修改的操作或返回值(我们将其视为可见结果 )。
在你的例子的情况下,个人结构将更有效,因为它包装POD数据并在逻辑上“结构化”它。