【问题标题】:pointer to objects within a class, C++ newbie question指向类中对象的指针,C++ 新手问题
【发布时间】:2010-10-13 19:07:45
【问题描述】:

为什么在 C++ 中,对于对象 A、B

//interface, case #1
class A {

B bb;
}

A::A()
{ //constructor
bb = B();
}


//interface, case #2
class A {

B *bb;
}

A::A()
{ //constructor
bb = new B();
}

为什么案例 #2 有效但 #1 无效?

编辑:我现在明白了。但是对于案例 #1,如果 A 的一个实例被释放,它的 bb 是否也会自动被释放?案例 #2 你必须显式调用 bb = NULL 对吗?

【问题讨论】:

  • 哪方面不行?
  • 发布真实代码,而不是伪代码。
  • bb = NULL 是不够的。 C++ 不是垃圾收集器。你需要调用 delete bb - 否则你是正确的:当你有 bb 作为成员时,它将与 A 一起自动释放,并且它的析构函数也会被自动调用。
  • A 不是对象。这是一堂课。关于你的问题,我建议阅读一本基本的 C++ 书籍。你真的需要从一个可靠的来源了解到,在 C++ 中设置一个指向 NULL 的指针对它所指向的内存没有任何作用。

标签: c++ class pointers oop


【解决方案1】:

两者都有语法错误,因此“不起作用”并不特定于其中一个。您还没有声明 A::A 构造函数,因此在尝试定义它时会出现语义错误。

如果 B 的完整声明在类 A 的声明之前可用,那么它将编译:

class B {};

//interface
class A {
    A();

    B bb;
};

A::A()
{ //constructor
    bb = B();
}

bb=B() 行中,已经构造了bb,因为它是A 的成员。然后您正在构造另一个B,然后将该B 的值复制到bb。这有点浪费。如果你想将参数传递给 bb 的构造函数,请使用初始化器。

另一方面,如果您不想将 B 的定义放在 A 的定义之前,并且只对它进行前向引用,那么编译器将无法计算出 B 对象的大小.它需要知道一个 B 有多大才能确定 A 中允许 bb 有多少空间。所以没有 B 就无法编译 A。

在第二种情况下,您改为使用指向 B 的指针。指针的大小相同,无论它们指向什么。这样编译器就可以计算出 A 有多大,并且只需要在实际使用 B 之前完整声明 B,例如通过在 new B() 表达式中调用 B 的构造函数。

【讨论】:

    【解决方案2】:

    在 C++ 中,如果你声明一个对象,你不需要初始化它。声明时调用构造函数。

    【讨论】:

      【解决方案3】:

      因为在编译时,创建 B 对象所需的内存量是未知的。因此,必须使用指向对象的指针,以便动态分配内存(当您调用 new 时)。

      【讨论】:

        【解决方案4】:

        显示的代码太不完整了。除非你给出 B 的定义,否则恐怕没人能回答。我的疯狂猜测是 B 类是不可复制的(例如私有运算符 =)。使用可复制的 B 类,您给出的示例应该可以工作。

        【讨论】:

          【解决方案5】:

          看起来您的“B”类隐藏了 operator=。否则,你写的应该编译。

          但是,这不是必需的。这应该有效:

          A::A() : bb()
          { //constructor
          }
          

          但是,由于您调用的是默认构造函数,因此无需显式声明它。这也很容易:

          A::A() // bb is automatically constructed, since it's a member variable and not a pointer.
          { 
          }
          

          【讨论】:

            【解决方案6】:

            如果类 B 的定义(不是声明)出现在类 A 的声明之前,那么你的 case#1 应该可以正常编译。

            正如Oxley所指出的,原因是C++需要知道B对象需要多少内存,而没有B的定义就无法做到。

            但是,如果您使用指针(如案例#2),则所需的内存量是已知的,因为所有指针占用相同的内存量(32 位或 64 位,具体取决于系统)。

            所以,对于案例#2,你只需要在 A 的定义之前声明 B。(注意:声明,而不是定义!当然,有定义也没有坏处,但不一定需要) .

            只是为了确保我不会混淆任何人,(嘿,也许我没有 C++ 术语)

            声明:声明一个类或函数的存在,但没有说明它是什么。

            例如

            class A; //forward declaration
            
            class C
            {
               A * a;
               // .....
            };
            

            definition:实际上定义了类或函数。虽然对于一个类来说,它不一定完全定义它,但它定义了它的所有成员。

            class A
            {
               int a;
               int b;
               void some_method(); //not defined here, only declared, but it's ok
            };
            

            【讨论】:

              【解决方案7】:

              我认为情况 A 必须有效,因为如果没有定义,编译器会提供一个复制构造函数。

              【讨论】:

                【解决方案8】:

                对我而言的问题是如何制作

                A::A()
                { //constructor
                bb = new B();
                }
                

                但没有新的。而且我认为问题中的代码不是真正的代码。正是为什么“新”有效,而简单的分配却无效。

                我的回答如下。如果我以错误的方式理解问题或答案本身是错误的 - 请告诉我。

                改变

                A::A()
                { //constructor
                bb = B();
                }
                

                A::A():
                    bb()
                {
                   // some logic
                }
                

                为了与“新”的情况保持一致,最好像这样实现

                A::A():
                    bb( new B() )
                {
                    // some logic
                }
                

                在这两种情况下,当“某些逻辑”开始执行时,您可以确定对象 bb 要么已初始化,要么将引发异常。

                为了使您的案例可编译,B 应该实现了分配运算符。

                您的编辑:您对案例 1 的猜测是正确的。在情况 2 中,您必须在类析构函数中调用 delete bb。

                请留言'-1'原因。我真的很困惑。

                【讨论】:

                • bb() 为 case#1 添加了什么?这对我来说似乎毫无意义,它没有任何以前不存在的东西。
                • @hasen j:问题是什么? :bb()是A初始化列表中BB调用的构造函数。
                • 所以?它不会在案例 1 中添加任何内容。bb 已经被定义为一个值,因此即使您没有明确地将其放入初始化列表中,它的默认构造函数也会被调用。
                • @hasen j:在这个简单的例子中。但是在整数类型或指针的情况下,或者在不是默认构造函数的情况下——这是最正确的方法。
                • @hasen j:将对象放在具有默认构造函数的初始化列表事件中是一个好习惯。问题是关于与“new B()”的正确对应关系,答案表明了这一点。
                猜你喜欢
                • 2018-10-02
                • 1970-01-01
                • 1970-01-01
                • 2023-02-09
                • 2011-02-11
                • 2019-06-17
                • 1970-01-01
                • 2019-10-11
                • 1970-01-01
                相关资源
                最近更新 更多