为什么不直接使用类字段呢
Why not use class fields directly?
所以我知道类和结构是数据的结构。类字段默认为私有字段,结构字段为公共字段。类似于C++的public: / int a;
和C#的public int a;
但是访问这些字段的其他方法是使它们私有并使用函数/方法。就像SetValue(int value){a = value;}
和GetValue() { return a; }
,或者我甚至听说过C#中新的酷炫{set; get;}
。
但是为什么许多人告诉我"其他人可以通过这种方式访问您的变量,所以请将其设为私有"。我不明白,把它们公开并只使用obj.a = 3;
,或者把它们私有并使用obj.SetValue(3);
,有什么区别?有人(甚至简短地)能解释一下有什么区别吗?当这些字段公开时,s.o.如何访问这些字段?
使用属性(getter和setter)而不是公共字段(公开实例变量)的原因有很多。其中一些是:
- 您可以允许对外部类进行只读访问(通过提供getter,但不提供setter)
- 您可以在访问该值时计算该值
- 您可以在不破坏现有代码的情况下更改访问/计算值的方式
- 您可以在不破坏现有代码的情况下更改内部表达
- 您可以覆盖子类中的属性,这在字段中是不可能的
- 等等
此外,使用属性并没有太多负面影响。所以利大于弊。
不同语言的答案不同。
在C++中,最好将属性设为私有,并提供getter和setter方法,因为C++提供常量正确性,并且不能在常量对象上调用setter。
在不提供常量正确性的C#中,除了简单地将所有属性提供为公共属性之外,做其他事情似乎毫无意义,因为一旦有了setter,就可以随时调用它。
但是,例如,如果属性是一个容器(例如List),该怎么办。那么,你可能只希望人们能够操作列表,而不希望将列表指针设置为新值,所以你可以这样定义属性:
class Contrived
{
private List<Things> m_List = new List<Things>();
public List<Things> LIST{ get {return m_List;} }
};
或者,你可能只希望人们能够检查列表,而不希望添加或删除列表中的内容:
class Contrived
{
private List<Things> m_List = new List<Things>();
public IEnumerable<Things> LIST{ get {return m_List;} }
};
通过这种方式,我们正朝着通过使用接口来伪造常量正确性的方向迈进,尽管IMO不如仅仅拥有一种常量正确的语言。;-)事实上,在上一个例子中,我们提供了一个不能修改的列表,但谁的contents可以修改,所以我们可以实现这样的属性(假设Things实现IUnmoddableThing):
class Contrived
{
private List<Things> m_List = new List<Things>();
public IEnumerable<IUnmoddableThing> LIST{
get
{
List<IUnmoddableThing> temp = new List<IUnmoddableThing>();
... copy m_List into temp ...
return temp;
} }
};
显然,这使我们能够比简单地公开成员属性拥有更多的控制权。那是在我们进入一处房产之前,我们实际上,也许。。。哦,通过定制的军用无线电携带的插座连接到远程数据库。。。
一旦您意识到setter在编译时可以被拒绝使用(在常量正确的语言中),getter/setter或get/set(取决于语言)可以做的不仅仅是简单的赋值或返回,它们就会非常强大。
范围界定-例如,这是一种告诉人们如何使用您的类的方法
任何公开的(或在C++中受保护的)内容都是您与类用户签订的合同的一部分
这意味着:
你不应该更改任何公共
您可以自由更改任何私有的东西,因为它被认为是类的实现细节
这种行为被广泛认为是"良好的编程方式",以便始终为类用户提供一个他可以自由使用的界面,而不必担心破坏其中的任何东西。这主要是为什么应该关闭类似访问器的方法中不断更改的类属性。当更改一个字段也会更改其他字段并对类产生影响时,它非常有用。此外,当你试图发现有人更改字段值时,它会派上用场;)
如果您以后想将该属性更改为数据库调用,该怎么办?这在有田地的情况下是不可能的。此外,如果您不希望属性可以从类外部设置,但不能在类内部设置,该怎么办?如果你想更改它,使你只能设置非null值,该怎么办?当值更改时,你能收到通知吗?你不能用一个场来做任何这些,也不能用getter+setter来做所有这些。
这是调用封装,允许您正确管理对类信息的访问:封装
封装并不总是需要的,但强烈建议隐藏类内部并防止未处理的数据管理,例如,您可以向setter添加一些检查,如果数据是公共的,则无法阻止:
int SomeClass::setPointer(char *point)
{
if(!point)
{
cout << "Trying to assign null pointer" << endl; //Or, better, throw exception
return error; // Or, better, throw exception
}
mPointer = point;
}
这是最基本的优势,但应该还有其他类似的:
- 您可以在每次修改主数据时自动更新相关的控制数据(例如,每次添加新元素时的数组大小)
- 样式:它更容易维护(例如,您更改成员的类型,您只需要在setter中添加一个强制转换,而不需要修改调用该字段的所有行)
- 提高可用性
- 在某些情况下,性能会更好
- 等等
其他答案列出了许多封装的充分理由。我只想补充一个最基本的原因:
- 保护类的不变量
如果你的类被设计为保持某种不变,你就不能给类的用户任何破坏它的方法。例如,如果你设计了一个容器,那么给用户写访问capacity
和size
之类的东西是不明智的。
此外,类中数据成员的值很少是独立的,直接访问(更改值)数据很容易使对象处于不一致的状态。
- 如何在QByteArray中放置和检索位字段而不会感到痛苦?
- 声明为类字段而不是全局变量
- 当指定初始化程序的顺序和字段声明不一致时,clang可以删除函数调用
- 为什么将值分配给一个位字段不给予相同的值
- 为什么 MSVC 和 GCC 不能使用具有默认值的字段初始化结构
- 为什么不直接使用类字段呢
- 字段具有不完整的类型:int*[]
- 字段具有不完整的类型 const char * []
- psinfo_tsolaris的字段中不包含完整的进程名称
- 为什么枚举与 Windows 中的位字段不兼容
- 原始数据包创建导致 IP 字段顺序不正确
- 为什么我应该使用 1 位位字段而不是布尔值
- 在C++类中使用指针作为成员字段是不是很傻
- 为什么我不能在"好友"运算符中使用"私人"字段?
- 在派生类中同时分配多个字段-为什么这样做有效
- 字段类型不完整
- 为什么常量 POD 对象中的字段本身不是常量?
- c++中的空字段和不存在的字段
- C++类相互引用( => 错误 + 字段"..."类型不完整)
- 字段具有不完全类型c++