为什么可以在复制构造函数中访问私有变量?


88

我了解到,只有在类中具有get函数时,我才能访问私有变量。但是,为什么我可以在复制构造函数中访问它呢?

例:

Field::Field(const Field& f)
{
  pFirst = new T[f.capacity()];

  pLast = pFirst + (f.pLast - f.pFirst);
  pEnd  = pFirst + (f.pEnd - f.pFirst);
  std::copy(f.pFirst, f.pLast, pFirst);
}

我的声明:

private:
  T *pFirst,*pLast,*pEnd;

因为复制构造函数默认情况下是类成员,其他一些构造函数也是如此。
DumbCoder

+ 53 / -0?谁为此投票?您还能如何复制它们?!(我们对非替代方案进行揭穿:为每个私人成员设置一个公共引用获取器?那么他们根本就不是私有的。为每个私人成员创建一个公共const&或按值获取器?然后它们只是“写私有”,&对于价值的浪费是资源,对不可复制的成员却失败。)我对这样一个空洞的问题的成功感到困惑,在完全忽略其含义的同时询问复制构造,没有答案使用基本逻辑来揭穿它。他们解释了干燥的技术,但是这个眨眼的问题有一个简单得多的答案
underscore_d

8
@underscore_d,“您还将如何复制它们?” 我认为这是一个非常奇怪的回应。就像回答“重力如何工作?” “还有其他事情会掉下去的!” 实际上,将类级别的封装与对象级别的封装混为一谈。您似乎觉得这是一个愚蠢的问题,答案应该很明显很有趣。请记住,使用Smalltalk(可以说是原型OO语言)进行封装实际上恰好在对象级别上起作用。
aioobe '16

@aioobe好点,谢谢。我的评论有点极端-也许那天咖啡机坏了。我感谢您指出为什么这个问题会流行,尤其是在那些使用其他(也许更多)OO语言的人中。实际上,可以说我的评论是“眨眼间”,因为我是从主要使用C ++编程的人的角度编写的。另外,喜欢重力的比喻!
underscore_d

Answers:


33

恕我直言,现有的答案在解释“原因”方面做得很差-过多地强调了行为的正确性。“访问修饰符在类级别而不在对象级别起作用。” - 是的,但是为什么?

这里的总体概念是,程序员被设计,编写和维护一个类,期望他们理解所需的OO封装并有权协调其实现。因此,如果您正在编写class X,那么您不仅在编码X x访问代码的方式来访问单个对象,而且还编码方式:

  • 派生类能够与之交互(通过可选的纯虚拟函数和/或受保护的访问),以及
  • 不同的X对象协作提供预期的行为,同时尊重设计中的后置条件和不变式。

这不仅是复制构造函数-很多操作可能涉及到您的类的两个或多个实例:如果您要进行比较,添加/乘法/除法,复制构造,克隆,分配等,那么通常情况是要么只是必须有权访问另一个对象中的私有数据和/或受保护的数据,要么希望它允许更简单,更快或通常更好的功能实现。

具体来说,这些操作可能想利用特权访问来执行以下操作:

  • (复制构造函数)在初始化程序列表中使用“ rhs”(右侧)对象的私有成员,因此成员变量本身是复制构造的,而不是默认构造的(即使合法),然后也被分配了(再次,如果合法)
  • 共享资源-文件句柄,共享内存段,shared_ptr引用数据等。
  • 取得事物的所有权,例如auto_ptr<>对正在建造的对象“移动”所有权
  • 复制以最佳可用状态构造新对象所需的私有“缓存”,校准或状态成员,而不必从头开始重新生成它们
  • 复制对象中保存的复制/访问诊断/跟踪信息,否则无法通过公共API进行访问,但是以后的异常对象或日志记录可能会使用该信息(例如,有关“原始”非复制构造实例的时间/情况的某些信息)建成)
  • 执行某些数据的更有效的复制:例如,对象可能具有例如一个unordered_map成员,但仅公开显示begin()end()迭代器-直接访问size()您可以reserve快速复制;更糟糕的是,如果他们只暴露at()insert()否则throw....
  • 将引用复制回客户端代码可能未知或仅写的父对象/协调对象/管理对象

2
我认为最大的“原因”是检查this == other访问是否每次访问other.x,如果访问修饰符在对象级别起作用,检查每次访问是否会花费巨大的运行时间。
aioobe '16

2
@aioobe我认为您的答案应该是正确的,更突出的方法。托尼·托尼(Tony Tony)的回答确实很好并且具有概念性,但如果我是一个博彩人,我敢打赌,您的回答是做出选择的实际历史原因。它不仅性能更高,而且更简单。对于Bjarne来说将是一个很大的问题!
尼尔·弗里德曼

我已标记了您的答案,因为它解释了背景;)
演示

@demonking,我认为此答案中给出的原因涵盖了为什么让私有数据对其他对象开放很方便。但是访问修饰符并不能使数据“足够开放”。它们的目的是使数据足够封闭以进行封装。(就方便而言,如果将私有变量放在公共位置,甚至会更好!)我用一个部分更新了我的答案,我认为该部分可以更好地解决实际原因
aioobe

@aioobe:旧注释,但是无论如何...“检查this == other每次访问是否other.x”-遗漏了点-如果other.x仅在运行时被接受,当等于时this.x,首先不会写太多指针other.x;编译器也可能会强迫if (this == other) ...this.x...您为要执行的操作编写代码。您的“便利性(如果私有变量是公共的,甚至更多)”的概念也没有抓住重点-标准定义的方式足够严格,可以进行适当的封装,但又不会造成不必要的麻烦。
托尼·德罗伊

108

访问修饰符在类级别而不在对象级别起作用。

即,同一类的两个对象可以互相访问私有数据。

为什么:

主要是由于效率。检查this == other每次访问other.x是否必需,访问修饰符是否在对象级别起作用,这将是不可忽略的运行时开销。

如果从范围上考虑,它在语义上也是逻辑上的:“在修改私有变量时,我需要记住多少代码?” –您需要牢记整个类的代码,这与运行时中存在的对象正交。

并且在编写副本构造函数和赋值运算符时非常方便。



10

为了理解答案,我想提醒您一些概念。

  1. 无论您创建多少个对象,该类在内存中只有一个副本。这意味着功能只能创建一次。但是,变量对于类的每个实例都是单独的。
  2. this 指针在调用时传递给每个函数。

现在是因为 this有了指针,函数才能够找到该特定实例的变量。不管它是否公开。可以在该函数内部访问它。现在,如果我们将指针传递给相同类的另一个对象。使用第二个指针,我们将能够访问私有成员。

希望这能回答您的问题。


6

复制构造函数是类的成员函数,因此可以访问类的数据成员,甚至是声明为“私有”的成员。


3
作为了解该语言的人,我理解您的意思。但是,如果我不懂该语言,我会以为您只是在向我重复这个问题。
圣哈辛托
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.