自定义类/对象的构造函数和初始化

时间:2012-03-21 10:07:39

标签: c++ constructor initialization

我可以想象这个问题已经被问到了,但实际上我找不到任何合适的解决方案,所以请原谅这是一个多余的问题。

我有一个自定义类

class myClass_A
{
public:
    myClass_A();          // Constructor
    myFunction_A();       // Some function from Class A
};

现在我有另一个自定义类,其成员类型为myClass_A

class myClass_B
{
public:
    myFunction_B();       // Some function from Class B

private:
    myClass_A m_instance; // Instance of Class A
}

现在myFunction_B()想要从myFunction_A()调用方法m_instance,如下所示:

myClass_B::myFunction_B()
{
    m_instance.myFunction_A();
}

现在,如果我编译我的代码(基本上就像我上面发布的示例),它将成功,没有任何警告或错误。所以我的问题是:

:一种。在这个例子中是否会调用构造函数?

B中。我真的可以从未初始化的对象中调用方法吗?

℃。假设没有调用构造函数,但我仍然可以从该对象调用方法 - >这意味着我的班级成员仍未初始化?

很抱歉,如果这些问题有点愚蠢,但我觉得我现在的速度很慢。

5 个答案:

答案 0 :(得分:7)

这些是非常好的和重要的问题。

关于A:

在执行构造函数体之前,C ++会生成代码,该代码会自动调用类的所有聚合(即成员)对象的默认构造函数。基本上,它所做的是转换以下代码:

class myClass_B {
public:
    myClass_B()
    {
        m_instance.foo();
        m_pInstance->foo();
    }
private:
    myClass_A m_instance;
    myClass_A* m_pInstance;
};

进入以下代码:

class myClass_B {
public:
    myClass_B()
        : m_instance()
        , m_pInstance()
    {
        m_instance.foo();
        m_pInstance->foo();
    }
private:
    myClass_A m_instance;
    myClass_A* m_pInstance;
};

编译器自动插入的两行称为initializer list,它在执行构造函数体之前调用每个聚合对象的默认构造函数。请注意,第二个m_pInstance()调用 pointer 的默认构造函数“,它会创建一个未初始化的指针;这几乎总是不是你想要的。请参阅下文,了解如何解决此问题。

现在让我们假设myClass_A的构造函数具有签名myClass_A(int someNumber),即它需要一个参数。然后,C ++ 无法自动生成myClass_B的初始化列表,因为它不知道要传递myClass_A的构造函数的数字。它会向你抛出一个编译器错误,可能会抱怨myClass_A缺少默认构造函数。您必须自己编写初始化列表,例如:

class myClass_B {
public:
    myClass_B()
        : m_instance(21)
        , m_pInstance(new myClass_A(21))
    {
        m_instance.foo();
        m_pInstance->foo();
    }
private:
    myClass_A m_instance;
    myClass_A* m_pInstance;
};

这是正确的代码,它为myClass_A构造函数调用参数someNumber的值为21。这也显示了如何正确初始化指针:使其指向一些新分配的对象。

关于B:

与其他人不同,你可以! (尝试一下)

但它会导致意外行为,这不是你想要的。 (包括它可能只在行星正确对齐时才能做你想要的。)它很可能会崩溃,但不能保证崩溃。这可能会导致您进行一些长时间的调试。如果您的编译器是智能的,它可能会识别出这个并且警告你,但它不会给你一个错误。

另请注意,对于具有默认构造函数的非指针聚合对象,将调用默认构造函数 ,您将一切顺利。使用内置类型或指针时会出现问题。这是使用未初始化的变量,并且是导致错误的最常见原因之一。如果您的代码做了一些非常奇怪的事情,请始终检查您是否初始化了所有变量。它应该成为一个反射,在任何成员变量的初始化列表中放入一个条目,即使它正在调用默认构造函数。事情清楚。

关于C:

是。有关详情,请参阅B.有趣的是,如果您调用的方法不使用“this”指针(这包括不使用任何属性变量而不调用任何使用属性变量的方法),则保证您的方法有效。在未初始化对象上调用方法时发生的情况是方法中的“this”对象(即所有属性变量)也是随机存储器。该方法的代码将执行但使用随机内存,这就是失败。

我希望这可以解决一些问题。

答案 1 :(得分:4)

  

在此示例中是否会调用构造函数?

是的
在调用构造函数myClass_B完成后,将调用myClass_A()的构造函数 通常,您将使用
myClass_B的构造函数中初始化my_class_A对象 的 Member Initializer List

  

我可以实际调用未初始化对象的方法吗?

创建对象时,将始终调用构造函数,除非构造对象,否则无法调用任何方法。这是构造函数创建对象并初始化它的目的 因此,如果你有一个对象,它永远不会被初始化。

如果您指的是指针,那么指针本身不是可以指向有效或无效对象的对象。对指向有效对象的指针(用于调用成员函数或其他)进行去反射将导致未定义行为

  

假设没有调用构造函数,但我仍然可以从该对象调用方法 - >这意味着我的班级成员仍未初始化?

第二个问题的答案回答了这个问题。

答案 2 :(得分:2)

类成员由编译器使用默认构造函数自动初始化,除非您在拥有类的构造函数initialization list中另行指定。所以是的,构造函数将被调用。

您可以通过以下方式自行测试:

  • 让构造函数打印一条消息(您将看到)或
  • 使构造函数成为私有,或者声明带有参数的构造函数,以便不再自动生成默认构造函数(编译器将拒绝编译,因为它不能再默认构造聚合对象)

如果您调用方法或访问尚未初始化的对象的成员或已经被破坏的对象(两种方案都可以设计),那么您有 undefined behavior

答案 3 :(得分:2)

一个。是的:成员的构造函数在包含类的构造函数之前调用。 B.是的,但结果是未定义的行为(=任何事情都可能发生;甚至看似工作。)。

答案 4 :(得分:0)

一个。当myClass_B类的对象被实例化时,将调用类myClass_A的构造函数。

B中。您无法调用未初始化对象的方法,例如

myClass_A *pObj;
pObj->myFunction_A();

会给你一个例外。

℃。如果没有完全构造对象,就不能成功地调用方法。