Nic*_*ton 34 c# c++ properties
我原来是来自C#的世界,我正在学习C++.我一直想知道在C++中获取和设置函数.在C#中,这些使用非常流行,而像Visual Studio这样的工具通过使它们变得非常容易和快速实现来促进使用.但是,在C++世界中似乎并非如此.
这是C#2.0代码:
public class Foo
{
private string bar;
public string Bar
{
get { return bar; }
set { bar = value; }
}
}
Run Code Online (Sandbox Code Playgroud)
或者,在C#3.0中:
public class Foo { get; set; }
Run Code Online (Sandbox Code Playgroud)
可能人们会说,那是什么意思呢?为什么不创建一个公共字段,然后在需要时将其变为属性; 老实说,我其实不确定.我只是出于好的做法,因为我已经看过很多次了.
现在因为我已经习惯了这样做,我觉得我应该把习惯延续到我的C++代码中,但这真的有必要吗?我没有像C#那样频繁地完成它.
无论如何,这是我收集的C++:
class Foo
{
public:
std::string GetBar() const; // Thanks for the tip Earwicker.
void SetBar(std::string bar);
private:
std::string bar;
}
const std::string Foo::GetBar()
{
return bar;
}
void Foo::SetBar(std::string bar)
{
// Also, I always wonder if using 'this->' is good practice.
this->bar = bar;
}
Run Code Online (Sandbox Code Playgroud)
现在,对我来说,这似乎是一大堆腿部工作; 考虑使用Visual Studio的工具,C#实现需要几秒钟的时间来实现,而C++花了我更长的时间来打字 - 我觉得它不值得努力,特别是当替代品是5行长时:
class Foo
{
public:
std::string Bar;
}
Run Code Online (Sandbox Code Playgroud)
从我收集到的,这些是优点:
缺点:
为什么我用较少的选票选择答案?我实际上非常接近选择veefu的答案 ; 然而,我个人的意见(显然是有争议的),就是对布丁的怂恿.
另一方面,我选择的答案似乎在争论双方; 我认为如果过度使用吸气剂和制定者是邪恶的(我的意思是,当它没有必要时会破坏商业模式),但为什么我们不应该有一个叫做的函数GetBalance()?
当然,这将比通用的多得多PrintBalance(); 如果我想以另一种方式向用户展示它而不是课程要求我怎么办?现在,在某种意义上GetBalance()可能没有足够的相关性来争辩说"吸气者和制定者是好的",因为它没有(或者可能,不应该)有一个伴随的制定者,并且说到这个,一个被称为的函数SetBalance(float f)可能是坏的(在我看来)因为它意味着函数的实现者必须在类的一侧操纵帐户,这不是一件好事.
mfa*_*kas 36
我认为在C++中提供访问器比在C#中更重要.
C++没有内置的属性支持.在C#中,您可以将公共字段更改为属性,而无需更改用户代码.在C++中,这更难.
对于较少的输入,您可以实现简单的setter/getter作为内联方法:
class Foo
{
public:
const std::string& bar() const { return _bar; }
void bar(const std::string& bar) { _bar = bar; }
private:
std::string _bar;
};
Run Code Online (Sandbox Code Playgroud)
不要忘记,吸气剂和制定者有点邪恶.
vee*_*efu 34
冒着争论的风险,我会回到我在阅读"Holub on Patterns"时首次遇到的反对观点.这是一个非常具有挑战性的观点,但在反思时对我有意义:
吸毒者和二传手是邪恶的
使用getter和setter与面向对象设计的基础相对立:数据抽象和封装.从长远来看,过度使用getter和setter会降低代码的敏捷性和可维护性.它们最终公开了类的底层实现,将实现细节锁定到类的接口中.
想象一下,你的'std :: string Foo :: bar'字段需要从std :: string更改为另一个字符串类,比如说,它更好地优化或支持不同的字符集.您需要更改私有数据字段,getter,setter以及调用这些getter和setter的此类的所有客户端代码.
而不是将您的类设计为"提供数据"和"接收数据",而是将它们设计为"执行操作"或"提供服务".问问自己为什么要编写"GetBar"功能.你在用这些数据做什么?也许您正在显示数据或对其进行一些处理.这个过程是否更好地暴露为Foo的方法?
这并不是说吸气剂和制定者没有他们的目的.在C#中我认为使用它们的根本原因是与Visual Studio GUI设计IDE接口,但是如果你发现自己用C++编写它们,最好退后一步,查看你的设计,看看是否有什么东西不见了.
我将尝试模拟一个例子来说明.
// A class that represents a user's bank account
class Account {
private:
int balance_; // in cents, lets say
public:
const int& GetBalance() { return balance_; }
void SetBalance(int b) { balance_ = b; }
};
class Deposit {
private:
int ammount_;
public:
const int& GetAmount() { return ammount_; }
void SetAmmount(int a) { _balance = a; }
};
void DoStuffWithAccount () {
Account a;
// print account balance
int balance = a.GetBalance();
std::cout << balance;
// deposit some money into account
Deposit d(10000);
a.SetBalance( a.GetBalance() + d.GetValue());
}
Run Code Online (Sandbox Code Playgroud)
不需要很长时间才能看到设计非常糟糕.
getter和setter使解决问题变得更加困难,因为客户端代码DoStuffWithAccount现在绑定到我们用于实现帐户余额的数据类型.
所以,让我们传递一下这段代码,看看我们可以改进什么
// A class that represents a user's bank account
class Account {
private:
float balance_;
public:
void Deposit(float b) { balance_ += b; }
void Withdraw(float w) { balance_ -= w; }
void DisplayDeposit(std::ostream &o) { o << balance_; }
};
void DoStuffWithAccount () {
Account a;
// print account balance
a.DisplayBalance(std::cout);
// deposit some money into account
float depositAmt = 1000.00;
a.Deposit(depositAmt);
a.DisplayBalance(std::cout);
}
Run Code Online (Sandbox Code Playgroud)
'浮动'是朝着正确方向迈出的一步.当然,您可以将内部类型更改为'float'并仍然支持getter/setter惯用法:
class Account {
private:
// int balance_; // old implementation
float balance_;
public:
// support the old interface
const int& GetBalance() { return (int) balance_; }
void SetBalance(int b) { balance_ = b; }
// provide a new interface for the float type
const float& GetBalance() { return balance_; } // not legal! how to expose getter for float as well as int??
void SetBalance(float b) { balance_ = b; }
};
Run Code Online (Sandbox Code Playgroud)
但是不需要花很长时间才能意识到getter/setter安排会使你的工作量增加一倍并使问题变得复杂,因为你需要支持使用int的代码和使用浮点数的新代码.存款功能使扩展存款类型的范围更加容易.
类帐户类可能不是最好的例子,因为"获得"帐户余额是帐户的自然操作.但总的来说,你必须小心吸气器和定位器.不要养成为每个数据成员编写getter和setter的习惯.如果你不小心,很容易暴露并锁定自己的实现.
Dan*_*ker 11
在你的例子中:
class Foo
{
public:
const std::string GetBar(); // Should this be const, not sure?
Run Code Online (Sandbox Code Playgroud)
你可能是这个意思:
std::string GetBar() const;
Run Code Online (Sandbox Code Playgroud)
将const末尾放在末尾意味着"此函数不会修改它被调用的Foo实例",因此在某种程度上它将其标记为纯粹的getter.
纯粹的getter经常在C++中出现.一个例子std::ostringstream是str()函数.标准库通常遵循对一对getter/setter函数使用相同函数名的模式 - str再次作为示例.
至于是否输入太多工作,是否值得 - 这似乎是一个奇怪的问题!如果您需要让客户访问某些信息,请提供getter.如果你不这样做,那就不要了.
[编辑]似乎我需要强调的是,setter需要验证参数并强制执行不变量,因此它们通常不像它们在这里那么简单.[/编辑]
不是全部,因为额外打字.我倾向于更频繁地使用它们,因为Visual Assist给了我"封装字段".
如果你只在类声明中实现内联的默认setter/getter(我倾向于这样做 - 尽管更复杂的setter移动到主体),但是腿部工作并不多.
一些说明:
constness :是的,getter应该是const.但是,如果按值返回,则返回值为const是没有用的.对于可能复杂的返回值,您可能希望使用const&尽管:
std::string const & GetBar() const { return bar; }
Run Code Online (Sandbox Code Playgroud)
Setter链接:许多开发人员喜欢这样修改setter:
Foo & SetBar(std::string const & bar) { this->bar = bar; return *this; }
Run Code Online (Sandbox Code Playgroud)
这允许调用多个setter:
Foo foo;
foo.SetBar("Hello").SetBaz("world!");
Run Code Online (Sandbox Code Playgroud)
然而,这并不是普遍接受的好事.
__declspec(property):Visual C++提供此非标准扩展,以便调用者可以再次使用属性语法.这会增加课堂上的练习,但会使调用者代码看起来更友好.
因此,总的来说,还有一些更多的工作,但是在C++中做出了一些决定.典型;)
对此没有严格的约定,就像在C#或Java中一样.许多C++程序员只是让变量公开,自己省事了.
正如其他答案所说,你不应该经常需要设置,并在某种程度上获得方法.
但是,如果你确实制造它们,就没有必要输入超过必要的东西:
class Foo
{
public:
std::string Bar() const { return bar; }
void Bar(const std::string& bar) { this->bar = bar; }
private:
std::string bar;
};
Run Code Online (Sandbox Code Playgroud)
在类中声明内联函数可以节省输入,并向编译器提示您希望内联函数.而且它的输入并不比C#等价物多.需要注意的一点是我删除了get/set前缀.相反,我们只有两个Bar()重载.这在C++中相当常见(毕竟,如果它不接受任何参数,我们知道它是getter,如果它需要一个参数,它就是setter.我们不需要这个名字告诉我们),它节省更多的打字.
我很少在自己的代码中使用 getter 和 setter。Veefu的回答对我来说看起来不错。
如果您坚持使用 getter 和/或 setter,您可以使用宏来减少样板。
#define GETTER(T,member) const T& Get##member() const { return member; }
#define SETTER(T,member) void Set##member(const T & value) { member = value; }
class Foo
{
public:
GETTER(std::string, bar)
SETTER(std::string, bar)
private:
std::string bar;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
72051 次 |
| 最近记录: |