C++ 类可以包含它自己类型的成员吗

use*_*630 -3 c++ header getter-setter

C++ 类是否有可能像我们在 Java 中一样包含自己类型的实例?例如,这样的事情:

public class A {
  private A a1;
  private A a2;
  
  A getA1(){
   return a1;
  }

  A getA2(){
   return a2;
  }

  void setA1(A a1){
   this.a1 = a1;
  }

  void setA2(A a2){
   this.a2 = a2;
  }
}
Run Code Online (Sandbox Code Playgroud)

现在我想在 C++ 中做同样的事情或解决方法。

Ami*_*rsh 6

是的,它在 C++ 中是可行的。但是语法会有点不同:

  1. this-> 代替 this.

  2. private:/public:而不是private/public每个成员

  3. 记得;在课程结束时

  4. A*作为成员(或std::uniqe_ptr<A>std::shared_ptr<A>std::weak_ptr<A>)。


第 1-3 项仅仅是语法。第 4 项是 Java 和 C++ 之间的本质区别:

  • 在 Java 中,对象变量是对对象的引用,而在 C++ 中,对象变量是一个值。这就是为什么你不能在 C++ 中保持你自己的直接成员,因为对象的大小将是无限的 (A 持有 A 的实际值,持有 A 的实际值,......递归)。

    在 Java 中,当 A 持有 A 时,它只持有对另一个 A的引用(是的,您仍然可以递归访问引用的 A,但它不是您大小的一部分,您只是持有对它的引用,它存储在其他地方在内存中。增加的大小只是参考的大小)。

    您可以在 C++ 中使用引用变量指针实现类似的语义,通过添加&引用或*指针:

    A& a2 = a1; // a2 is a reference to A, assigned with a reference to a1
                // note that a1 above is assumed to be also of type A&
    
    A* a2 = a1; // a2 is a pointer to A, assigned with the address stored in a1
                // note that a1 above is assumed to be also of type A*
    
    Run Code Online (Sandbox Code Playgroud)
  • Java 垃圾收集器回收未使用的内存,而在 C++ 中,程序员需要处理它,可能使用 C++ 工具,例如智能指针

  • Java Garbage Collector 通过Trace by Reachability回收未使用的内存,C++ 智能指针基于作用域生存期。此外,C++shared_ptr基于引用计数,这有其优点,但受引用周期的影响,可能会发生内存泄漏,应通过正确设计代码来避免这种情况。


C++ 版本的“保持自己”可能看起来像下面的任何一个(或它们的变体),具体取决于确切的需要:

选项 1 - A持有不拥有a1 和 a2

class A {
   A* a1 = nullptr;
   A* a2 = nullptr;

public: 
   A* getA1(){
      return a1;
   }

   A* getA2(){
     return a2;
   }

   void setA1(A* a1){
     this->a1 = a1;
   }

   void setA2(A* a2){
     this->a2 = a2;
   }
};
Run Code Online (Sandbox Code Playgroud)

选项 2 - A拥有a1 和 a2 作为唯一资源

class A {
   std::unique_ptr<A> a1 = nullptr;
   std::unique_ptr<A> a2 = nullptr;

public: 
   A* getA1(){
      return a1.get();
   }

   A* getA2(){
     return a2.get();
   }

   void setA1(std::unique_ptr<A> a1){
     this->a1 = std::move(a1);
   }

   void setA2(std::unique_ptr<A> a2){
     this->a2 = std::move(a2);
   }
};
Run Code Online (Sandbox Code Playgroud)

选项 3 - A持有a1 和 a2 作为共享资源*

*需要确保避免循环所有权泄漏。

class A {
   std::shared_ptr<A> a1 = nullptr;
   std::shared_ptr<A> a2 = nullptr;

public: 
   auto getA1(){
      return a1;
   }

   auto getA2(){
     return a2;
   }

   void setA1(std::shared_ptr<A> a1){
     this->a1 = a1;
   }

   void setA2(std::shared_ptr<A> a2){
     this->a2 = a2;
   }
};
Run Code Online (Sandbox Code Playgroud)

选项 4 - A持有指向a1 和 a2 的弱指针*

*的选项std::weak_ptr在可能的循环依赖的情况下是相关的,a1 和 a2 在其他地方拥有并且可能不存在。

class A {
   std::weak_ptr<A> a1 = nullptr;
   std::weak_ptr<A> a2 = nullptr;

public: 
   std::shared_ptr<A> getA1(){
      return a1.lock();
   }

   std::shared_ptr<A> getA2(){
     return a2.lock();
   }

   void setA1(std::shared_ptr<A> a1){
     this->a1 = a1;
   }

   void setA2(std::shared_ptr<A> a2){
     this->a2 = a2;
   }
};
Run Code Online (Sandbox Code Playgroud)

选项 4 代码示例:http : //coliru.stacked-crooked.com/a/92d6004280fdc147


请注意,使用A&(对 A 的引用)作为成员不是一种选择,因为在 C++ 中引用变量比天主教婚礼强,它们在变量的生命周期内无法重新分配给另一个引用。并且它们必须在出生时分配给有效的参考。

但是,如果a1a2是在对象出生时已知的,在对象的生命周期内永远不会改变并保持活动状态,那么以下选项也是可能的:

选项 5 - A保存对 a1 和 a2 的引用*

*此选项主要是为了表明可以保存引用,但在大多数情况下,指针选项(如选项 1)或 const 指针成员会更合适。

class A {
   A& a1;
   A& a2;

public:
   A(A& a1, A& a2): a1(a1), a2(a2) {}

   // using ref to self as a placeholder
   // to allow the creation of "the first A"  
   A(): a1(*this), a2(*this) {}
  
   A& getA1(){
      return a1;
   }

   A& getA2(){
      return a2;
   }
};

int main() {
   A a1;
   A a2(a1, a1);
}
Run Code Online (Sandbox Code Playgroud)

下面的最后一个也是最后一个选项主要是提出继续执行选项 5 并允许更改 A 持有的参考的可能性。

从 C++20 开始,此选项是可能的。但是,需要注意的是,为此目的使用指针很可能是更好的选择。

选项 5b - A持有对 a1 和 a2 的引用,并允许设置!*

*从 C++20 开始,注意这个选项主要是为了展示可能性,指针在这里可能是更好的选择。

class A {
   // all same as in option 5
public:
   void set(A& a1, A& a2){
      A other(a1, a2);
      // placement new that changes internal ref
      // is valid since C++20
      new (this) A(other);
   }
};
Run Code Online (Sandbox Code Playgroud)

选项 5b 的代码:http : //coliru.stacked-crooked.com/a/43adef3bff619e99

另请参阅:为什么我可以为引用分配新值,以及如何使引用引用其他内容?

  • “A* 作为会员”?好的,但这需要更改成员函数。另外,成员签名的类型不完整怎么办?在OP明确他们想要什么“语义”之前,我不会建议“语法”。 (3认同)