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

Posted

技术标签:

【中文标题】为啥我可以在复制构造函数中访问私有变量?【英文标题】:Why can I access private variables in the copy constructor?为什么我可以在复制构造函数中访问私有变量? 【发布时间】:2011-05-06 05:51:58 【问题描述】:

我了解到我永远无法访问私有变量,只能使用类中的 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;

【问题讨论】:

【参考方案1】:

访问修饰符适用于类级别,而不适用于对象级别

即同一类的两个对象可以访问彼此的私有数据。

为什么:

主要是因为效率。每次访问 other.x 时检查 this == other 是否是不可忽略的运行时开销,如果访问修饰符在对象级别工作,则必须这样做。

如果您从范围界定的角度来考虑,这在语义上也是合乎逻辑的:“在修改私有变量时,我需要记住多大的代码部分?” – 您需要牢记整个类的代码,这与运行时中存在哪些对象是正交的。

而且在编写拷贝构造函数和赋值运算符时非常方便。

【讨论】:

Re:“检查this == other 是否是一个不可忽略的运行时开销”:我认为这个检查总是可以在编译时完成。如果访问是在对象级别而不是类级别,我会更喜欢它。 你想编译other = Math.random() > 0.5 ? this : somethingElse; print(other.privateField);吗? 我不知道,它有什么作用,需要解释一下吗? 它访问随机选择的对象上的私有字段。所选对象是this 或称为somethingElse 的引用。 我明白了。不,我不认为,我希望编译它。我知道在 C++ 中改变这种行为几乎是不可能的,因为标准是在编写时包含这种行为的,所以上帝知道万一改变了会发生什么。我只是说,当我编写我的第一个复制构造函数时,这种行为让我非常惊讶,我更喜欢对象级别的隐私,因为这样我可以确定除了this 之外真的没有人可以更改变量。【参考方案2】:

恕我直言,现有答案在解释其中的“原因”方面做得很差 - 过于关注重申哪些行为是有效的。 “访问修饰符在类级别上起作用,而不是在对象级别上。” - 是的,但为什么呢?

这里的总体概念是,设计、编写和维护一个类的程序员应该理解所需的 OO 封装并有权协调其实现。因此,如果您正在编写 class X,那么您不仅要编码单个 X x 对象如何被可以访问它的代码使用,还要编码如何:

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

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

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

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

【讨论】:

我认为最大的“为什么”是每次访问 other.x 时检查 this == other 是否会产生巨大的运行时开销,如果访问修饰符在对象级别上工作,则必须这样做. @aioobe 我认为您的答案应该更加突出。虽然托尼的回答非常好而且概念化,但如果我是一个赌徒,我敢打赌你的回答是选择的实际历史原因。它不仅性能更高,而且更简单。这对 Bjarne 来说是个好问题! 我已经标记了你的答案,因为它解释了背景;) @demonking,我认为这个答案中给出的原因涵盖了为什么让私有数据对其他对象开放是方便的。但是访问修饰符并不意味着使数据足够“公开”。它们的目的是使数据封闭足以封装。 (就方便而言,如果私有变量是公开的,那会更好更好!)我用我认为更好地解决实际为什么. @aioobe:旧 cmets,但无论如何...“每次访问 this == other 时检查 other.x” - 没有抓住重点 - 如果 other.x 仅在运行时被接受,则相当于this.x,首先不会有太多指针写other.x;编译器可能会强迫您为您要执行的任何操作编写if (this == other) ...this.x...。您的“方便(如果私有变量是公共的,则更方便)” 概念也没有抓住重点——标准定义的方式足够严格,允许正确封装,但不会造成不必要的不​​便.【参考方案3】:

您可以从类中访问类的私有成员,甚至是其他实例的私有成员。

【讨论】:

【参考方案4】:

为了理解答案,我想提醒你几个概念。

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

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

希望这能回答你的问题。

【讨论】:

【参考方案5】:

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

【讨论】:

作为懂语言的人,我明白你的意思。但是,如果我不懂语言,我会以为你只是在向我重复这个问题。【参考方案6】:

为什么制作那个编译器的人允许这种行为, 我们可以在复制构造函数或类中的任何方法中看到对象的隐藏成员(其类型与您访问隐藏成员的类相同)

答案:因为你在班级里就意味着你是班级的建造者或设计者,也意味着你知道 strong> 该类中的所有数据成员和方法,这就是他允许这种行为的原因,因为您构建了这个类,您对它了如指掌,不像该类的用户,他们不像您一样了解该类的每一件事。

隐藏数据成员或方法的想法,以帮助该类的用户,而不是将他们与不重要的事物混淆。

就是这样。

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于为啥我可以在复制构造函数中访问私有变量?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们需要私有构造函数?

为啥这里的 String 构造函数应该被保护而不是私有?

如果构造函数在私有部分,为啥我们不能创建对象?

为啥在通过 const 引用传递临时值时调用复制构造函数?

Java 类中的哪些变量构造函数可以访问?

两种创建私有变量私有方法的方法