C++ 类是否可以包含其自身类型的成员 [英] Can a C++ class include a member of its own type

查看:34
本文介绍了C++ 类是否可以包含其自身类型的成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C++ 类是否可以像在 Java 中一样包含自己类型的实例?例如,像这样:

Is it possible for a C++ class to include an instance of its own type the same way we can in Java? For example, something like this:

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;
  }
}

现在我想要相同的东西或 C++ 中的解决方法.

Now I want the same thing or a workaround in C++.

推荐答案

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

Yes, it's doable in C++. But the syntax would be a little different:

  1. this-> 而不是 this.

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

private:/public: instead of private/public per member

记得在类的末尾加上 ;

A* 作为成员(或 std::uniqe_ptrstd::shared_ptr 或 <代码>std::weak_ptr).

A* as member (or std::uniqe_ptr<A> or std::shared_ptr<A> or std::weak_ptr<A>).


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


Items 1-3 are merely syntax. Item 4 is an essential difference between Java and C++:

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

  • In Java an object variable is a reference to the object while in C++ an object variable is a value. This is why you can't hold in C++ a direct member of yourself, as is, the size of the object would be infinite (A holding an actual value of A, holding an actual value of A, ... recursively).

在 Java 中,当 A 持有 A 时,它只是持有 a 引用 到另一个 A (是的,您仍然可以递归访问引用的 A,但它不是您的大小的一部分, 你只是持有对它的引用,它存储在内存中的其他地方.除了你的大小只是引用的大小).

In Java when A holds an A, it just holds a reference to the other A (yes, you can still access recursively the referenced A, but it is not part of your size, you just hold a reference to it, it is stored elsewhere in memory. The addition to your size is just the size of a reference).

您可以在 C++ 中使用 reference variablespointers 实现类似的语义,方法是为引用添加 &* 用于指针:

You can achieve similar semantics in C++ with reference variables or pointers, by adding & for a reference or * for a pointer:

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*

  • Java 垃圾收集器回收未使用的内存,而在 C++ 中,程序员需要处理它,可能使用 C++ 工具,例如 智能指针.

    Java 垃圾收集器通过 Trace by Reachability、C++ 智能指针回收未使用的内存是基于作用域生命周期的.此外,C++ shared_ptr 基于 引用计数 有其优势,但会受到引用周期的影响,可能存在内存泄漏,应通过正确设计代码来避免这种情况.

    Java Garbage Collector reclaims unused memory via Trace by Reachability, C++ smart pointers are based on scope lifetime. Additionally, C++ shared_ptr is based on reference counting which has its advantages, but is subject to reference cycles possible leak of memory, which should be avoided with proper design of your code.

    坚持自己"的 C++ 版本可能看起来像以下任何一种(或它们的变体),具体取决于具体需要:

    The C++ version of "holding myself" may look like any of the below (or variations of them), depending on the exact need:

    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;
       }
    };
    

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

    Option 2 - A owns a1 and a2 as unique resources

    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);
       }
    };
    

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

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

    Option 3 - A holds a1 and a2 as shared resources*

    * need to make sure you avoid cyclic ownership leak.

    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;
       }
    };
    

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

    * std::weak_ptr 的选项在可能的循环依赖的情况下是相关的,a1 和 a2 在别处拥有并且可能不存在.

    Option 4 - A holds weak pointers to a1 and a2*

    * the option of std::weak_ptr is relevant in case of possible cyclic dependency, a1 and a2 are owned elsewhere and might not be alive.

    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;
       }
    };
    

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

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

    Note that using A& (reference to A) as a member, is not an option, as in C++ reference variables are stronger than Catholic wedding, they're for the lifetime of the variable without any way to reassign to another reference. And they must be assigned to a valid reference when born.

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

    However, if a1 and a2 are known when the object is born, never change and stay alive for the duration of the object's lifetime, then the following option is also possible:

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

    * this option is mainly to show that it is possible to hold references, however in most cases a pointer option (like option 1), or a const pointer member, would be more suitable.

    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);
    }
    


    下面的最后一个也是最后一个选项,主要是提出继续使用选项 5 并允许更改 A 持有的引用的可能性.


    The last and final option, below, is mainly to present the possibility of going forward with option 5 and allowing the change of the reference held by A.

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

    This option is possible since C++20. However, it is to be noted that using a pointer for this purpose would most probably be a better choice.

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

    *since C++20, note that this option is mainly to show the possibility, pointers would probably be a better choice here.

    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);
       }
    };
    

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

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

    这篇关于C++ 类是否可以包含其自身类型的成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆