C++ getter/setter 编码风格
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/760777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ getters/setters coding style
提问by Alex
I have been programming in C# for a while and now I want to brush up on my C++ skills.
我已经用 C# 编程一段时间了,现在我想复习一下我的 C++ 技能。
Having the class:
上课:
class Foo
{
const std::string& name_;
...
};
What would be the best approach (I only want to allow read access to the name_ field):
最好的方法是什么(我只想允许读取 name_ 字段):
- use a getter method:
inline const std::string& name() const { return name_; }
- make the field public since it's a constant
- 使用 getter 方法:
inline const std::string& name() const { return name_; }
- 公开该字段,因为它是一个常量
Thanks.
谢谢。
采纳答案by Mr Fooz
It tends to be a bad idea to make non-const fields public because it then becomes hard to force error checking constraints and/or add side-effects to value changes in the future.
公开非常量字段往往是一个坏主意,因为这样就很难强制错误检查约束和/或在将来对值更改添加副作用。
In your case, you have a const field, so the above issues are not a problem. The main downside of making it a public field is that you're locking down the underlying implementation. For example, if in the future you wanted to change the internal representation to a C-string or a Unicode string, or something else, then you'd break all the client code. With a getter, you could convert to the legacy representation for existing clients while providing the newer functionality to new users via a new getter.
在您的情况下,您有一个 const 字段,因此上述问题不是问题。使其成为公共字段的主要缺点是您正在锁定底层实现。例如,如果将来您想将内部表示更改为 C 字符串或 Unicode 字符串或其他内容,那么您将破坏所有客户端代码。使用 getter,您可以转换为现有客户端的旧表示,同时通过新的 getter 向新用户提供更新的功能。
I'd still suggest having a getter method like the one you have placed above. This will maximize your future flexibility.
我仍然建议使用一种类似于您上面放置的 getter 方法。这将最大限度地提高您未来的灵活性。
回答by j_random_hacker
Using a getter method is a better design choice for a long-lived class as it allows you to replace the getter method with something more complicated in the future. Although this seems less likely to be needed for a const value, the cost is low and the possible benefits are large.
对于长期存在的类,使用 getter 方法是更好的设计选择,因为它允许您在将来用更复杂的东西替换 getter 方法。尽管这对于 const 值来说似乎不太可能需要,但成本低且可能带来的好处很大。
As an aside, in C++, it's an especially good idea to give both the getter and setter for a member the same name, since in the future you can then actually change the the pair of methods:
顺便说一句,在 C++ 中,给一个成员的getter 和 setter 赋予相同的 name是一个特别好的主意,因为将来你可以实际更改这对方法:
class Foo {
public:
std::string const& name() const; // Getter
void name(std::string const& newName); // Setter
...
};
Into a single, public member variable that defines an operator()()
for each:
到单个公共成员变量中,operator()()
为每个成员定义一个:
// This class encapsulates a fancier type of name
class fancy_name {
public:
// Getter
std::string const& operator()() const {
return _compute_fancy_name(); // Does some internal work
}
// Setter
void operator()(std::string const& newName) {
_set_fancy_name(newName); // Does some internal work
}
...
};
class Foo {
public:
fancy_name name;
...
};
The client code will need to be recompiled of course, but no syntax changes are required! Obviously, this transformation works just as well for const values, in which only a getter is needed.
客户端代码当然需要重新编译,但不需要更改语法!显然,这种转换也适用于 const 值,其中只需要一个 getter。
回答by chrish
As an aside, in C++, it is somewhat odd to have a const reference member. You have to assign it in the constructor list. Who owns the actually memory of that object and what is it's lifetime?
顺便说一句,在 C++ 中,有一个 const 引用成员有点奇怪。您必须在构造函数列表中分配它。谁拥有该对象的实际内存,它的生命周期是多少?
As for style, I agree with the others that you don't want to expose your privates. :-) I like this pattern for setters/getters
至于风格,我同意其他人的观点,你不想暴露你的隐私。:-) 我喜欢这种用于 setter/getter 的模式
class Foo
{
public:
const string& FirstName() const;
Foo& FirstName(const string& newFirstName);
const string& LastName() const;
Foo& LastName(const string& newLastName);
const string& Title() const;
Foo& Title(const string& newTitle);
};
This way you can do something like:
通过这种方式,您可以执行以下操作:
Foo f;
f.FirstName("Jim").LastName("Bob").Title("Programmer");
回答by Lambage
I think the C++11 approach would be more like this now.
我认为 C++11 方法现在更像是这样。
#include <string>
#include <iostream>
#include <functional>
template<typename T>
class LambdaSetter {
public:
LambdaSetter() :
getter([&]() -> T { return m_value; }),
setter([&](T value) { m_value = value; }),
m_value()
{}
T operator()() { return getter(); }
void operator()(T value) { setter(value); }
LambdaSetter operator=(T rhs)
{
setter(rhs);
return *this;
}
T operator=(LambdaSetter rhs)
{
return rhs.getter();
}
operator T()
{
return getter();
}
void SetGetter(std::function<T()> func) { getter = func; }
void SetSetter(std::function<void(T)> func) { setter = func; }
T& GetRawData() { return m_value; }
private:
T m_value;
std::function<const T()> getter;
std::function<void(T)> setter;
template <typename TT>
friend std::ostream & operator<<(std::ostream &os, const LambdaSetter<TT>& p);
template <typename TT>
friend std::istream & operator>>(std::istream &is, const LambdaSetter<TT>& p);
};
template <typename T>
std::ostream & operator<<(std::ostream &os, const LambdaSetter<T>& p)
{
os << p.getter();
return os;
}
template <typename TT>
std::istream & operator>>(std::istream &is, const LambdaSetter<TT>& p)
{
TT value;
is >> value;
p.setter(value);
return is;
}
class foo {
public:
foo()
{
myString.SetGetter([&]() -> std::string {
myString.GetRawData() = "Hello";
return myString.GetRawData();
});
myString2.SetSetter([&](std::string value) -> void {
myString2.GetRawData() = (value + "!");
});
}
LambdaSetter<std::string> myString;
LambdaSetter<std::string> myString2;
};
int _tmain(int argc, _TCHAR* argv[])
{
foo f;
std::string hi = f.myString;
f.myString2 = "world";
std::cout << hi << " " << f.myString2 << std::endl;
std::cin >> f.myString2;
std::cout << hi << " " << f.myString2 << std::endl;
return 0;
}
I tested this in Visual Studio 2013. Unfortunately in order to use the underlying storage inside the LambdaSetter I needed to provide a "GetRawData" public accessor which can lead to broken encapsulation, but you can either leave it out and provide your own storage container for T or just ensure that the only time you use "GetRawData" is when you are writing a custom getter/setter method.
我在 Visual Studio 2013 中对此进行了测试。不幸的是,为了使用 LambdaSetter 中的底层存储,我需要提供一个“GetRawData”公共访问器,这可能会导致封装损坏,但您可以将其排除在外并提供您自己的存储容器或者只是确保您唯一一次使用“GetRawData”是在编写自定义 getter/setter 方法时。
回答by Dan Breslau
Even though the name is immutable, you may still want to have the option of computing it rather than storing it in a field. (I realize this is unlikely for "name", but let's aim for the general case.) For that reason, even constant fields are best wrapped inside of getters:
即使名称是不可变的,您可能仍然希望选择计算它而不是将其存储在字段中。(我意识到这对于“name”来说不太可能,但让我们针对一般情况。)因此,即使是常量字段也最好包含在 getter 中:
class Foo {
public:
const std::string& getName() const {return name_;}
private:
const std::string& name_;
};
Note that if you were to change getName()
to return a computed value, it couldn't return const ref. That's ok, because it won't require any changes to the callers (modulo recompilation.)
请注意,如果您要更改getName()
为返回计算值,则无法返回 const ref。没关系,因为它不需要对调用者进行任何更改(模重新编译。)
回答by David Thornley
Avoid public variables, except for classes that are essentially C-style structs. It's just not a good practice to get into.
避免使用公共变量,除了本质上是 C 风格结构的类。进入这不是一个好习惯。
Once you've defined the class interface, you might never be able to change it (other than adding to it), because people will build on it and rely on it. Making a variable public means that you need to have that variable, and you need to make sure it has what the user needs.
一旦你定义了类接口,你可能永远无法改变它(除了添加它),因为人们会在它的基础上构建并依赖它。将变量设为公开意味着您需要拥有该变量,并且您需要确保它具有用户所需的内容。
Now, if you use a getter, you're promising to supply some information, which is currently kept in that variable. If the situation changes, and you'd rather not maintain that variable all the time, you can change the access. If the requirements change (and I've seen some pretty odd requirements changes), and you mostly need the name that's in this variable but sometimes the one in that variable, you can just change the getter. If you made the variable public, you'd be stuck with it.
现在,如果您使用 getter,您承诺提供一些当前保存在该变量中的信息。如果情况发生变化,并且您不想一直维护该变量,则可以更改访问权限。如果需求发生变化(并且我看到了一些非常奇怪的需求变化),并且您主要需要此变量中的名称,但有时是该变量中的名称,您只需更改 getter。如果你把变量公开,你就会被它困住。
This won't always happen, but I find it a lot easier just to write a quick getter than to analyze the situation to see if I'd regret making the variable public (and risk being wrong later).
这不会总是发生,但我发现仅仅编写一个快速的 getter 比分析情况以查看我是否会后悔将变量公开(并冒着以后出错的风险)容易得多。
Making member variables private is a good habit to get into. Any shop that has code standards is probably going to forbid making the occasional member variable public, and any shop with code reviews is likely to criticize you for it.
将成员变量设为私有是一个很好的习惯。任何有代码标准的商店可能会禁止将偶尔的成员变量公开,任何有代码的商店都可能因此批评你。
Whenever it really doesn't matter for ease of writing, get into the safer habit.
每当写作的难易程度真的无关紧要时,请养成更安全的习惯。
回答by lama12345
Collected ideas from multiple C++ sources and put it into a nice, still quite simple example for getters/setters in C++:
从多个 C++ 源中收集想法并将其放入一个很好的、仍然非常简单的 C++ 中的 getter/setter 示例:
class Canvas { public:
void resize() {
cout << "resize to " << width << " " << height << endl;
}
Canvas(int w, int h) : width(*this), height(*this) {
cout << "new canvas " << w << " " << h << endl;
width.value = w;
height.value = h;
}
class Width { public:
Canvas& canvas;
int value;
Width(Canvas& canvas): canvas(canvas) {}
int & operator = (const int &i) {
value = i;
canvas.resize();
return value;
}
operator int () const {
return value;
}
} width;
class Height { public:
Canvas& canvas;
int value;
Height(Canvas& canvas): canvas(canvas) {}
int & operator = (const int &i) {
value = i;
canvas.resize();
return value;
}
operator int () const {
return value;
}
} height;
};
int main() {
Canvas canvas(256, 256);
canvas.width = 128;
canvas.height = 64;
}
Output:
输出:
new canvas 256 256
resize to 128 256
resize to 128 64
You can test it online here: http://codepad.org/zosxqjTX
你可以在这里在线测试:http: //codepad.org/zosxqjTX
PS: FO Yvette <3
PS:FO 伊薇特 <3
回答by Abhay
From the Design Patterns theory; "encapsulate what varies". By defining a 'getter' there is good adherence to the above principle. So, if the implementation-representation of the member changes in future, the member can be 'massaged' before returning from the 'getter'; implying no code refactoring at the client side where the 'getter' call is made.
来自设计模式理论;“封装不同的东西”。通过定义“吸气剂”,可以很好地遵守上述原则。因此,如果将来成员的实现表示发生变化,则可以在从“getter”返回之前对成员进行“按摩”;暗示在进行“getter”调用的客户端没有代码重构。
Regards,
问候,