Конструктор и инициализация пользовательских классов / объектов

я мог бы представить, что этот вопрос уже задан, но я на самом деле не мог найти подходящего решения, поэтому, пожалуйста, извините, если это избыточный вопрос.

у меня есть пользовательский класс

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

теперь, если я скомпилирую свой код ( который в основном похож на пример, который я опубликовал выше ), он будет успешным без каких-либо предупреждений или ошибок. Так что мои вопросы будут:

A. будет ли вызван конструктор в этом примере?

Б. Я могу вызывать методы из неинициализированного объекта?

C. предполагая, что конструктор не вызывается, но я все еще могу вызвать методы из этого объекта -> это означает, что члены моего класса не являются инициализирован?

Извините, если эти вопросы немного глупые, но я чувствую, что я медленно соображаю сейчас.

5 ответов


это очень хорошие и важные вопросы.

Относительно 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;
};

две строки компилятора автоматически вставленные называются список инициализатора, он вызывает конструктор по умолчанию для каждого объекта aggregate до выполняется тело вашего конструктора. Обратите внимание, что второй, m_pInstance() вызывает " конструктор по умолчанию указатель", которая создает неинициализированный указатель ; это почти всегда не то, что вы хотите. См. ниже о том, как это исправить.

теперь предположим, что конструктор myClass_A имеет подпись myClass_A(int someNumber), т. е. требуется аргумент. Затем, С++ не может автоматически генерировать список инициализатора myClass_B как он не знает, какой номер передать 'ы. Он бросит вам ошибку компилятора, вероятно, жалуясь на отсутствующий конструктор по умолчанию для 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 конструктор со значением 21 для параметр someNumber. Это также показывает, как правильно инициализировать указатель: сделать его указывающим на какой-то новый выделенный объект.

Относительно B:

в отличие от некоторых других говорят, можно! (Попробуйте)

но это приводит к неожиданному поведению, которое является не то, что вы хотите. (Включая то, что он может делать то, что вы хотите, только когда планеты выровнены правильно.) Это, скорее всего, сбой, но не гарантируется сбой. Это может привести вас к некоторым длинные отладочные ночи. Если ваш компилятор умный, он может распознать это и предупредить вы, но это не даст вам ошибку.

также обратите внимание, что для агрегатных объектов без указателя, которые имеют конструктор по умолчанию, конструктор по умолчанию будет позвоните, и все будет хорошо. Проблема возникает, когда вы используете встроенные типы или указатели. Это использование неинициализированных переменных, и является одной из наиболее частых причин для ошибки. Если ваш код делает что-то совершенно странное, всегда проверяйте, инициализировали ли вы все свои переменные. Это должно стать рефлексом, чтобы поместить запись в список инициализаторов для любой переменной-члена, даже если она вызывает конструктор по умолчанию. Проясняет ситуацию.

Относительно C:

да. См. B для деталей. Интересно, что если вызываемый вами метод не использует указатель "this" (это включает в себя не использование переменной атрибута и не вызов метода, который использует переменную атрибута), ваш метод гарантированно работает. Что происходит, когда вы вызываете метод на неинициализированном объекте, так это то, что" этот " объект в методе (т. е. все переменные атрибута тоже) является случайной памятью. Код метода будет выполняться, но использовать случайную память, и это то, что не удается.

надеюсь, это немного прояснит ситуацию.


будет ли конструктор вызываться в этом примере?

да
Конструкторы для myClass_B будет вызываться после вызова конструктора myClass_A() завершено.
Обычно вы инициализируете myClass_B объект в конструкторе my_class_A С помощью
Список Инициализаторов Членов.

могу ли я на самом деле вызывать методы из неинициализированного объекта?

когда объект создается, его конструктор всегда будет вызываться, если вы не создадите объект, вы не можете вызвать какой-либо метод на нем. Это сама цель конструкторов-создать объект и инициализировать его.
Так что если у вас объект не инициализирован.

если вы имеете в виду указатели, указатели сами по себе не являются объектами, они могут указывать на допустимый или недопустимый объект. Derefencing указатель(для вызова функции-члена или whatever) , не указывая на допустимый объект, приведет к Неопределенное Поведение.

предполагая, что конструктор не вызывается, но я все еще могу вызывать методы этого объекта -это значит, что члены класса не инициализируются?

ответ на второй вопрос отвечает на этот.


A. да: конструкторы членов вызываются перед конструктором содержащего класса. B. Да, но результатом является неопределенное поведение (= все может произойти; даже кажется, что работает.).


члены класса автоматически инициализируются компилятором с помощью конструктора по умолчанию, если в конструкторе класса-владельца не указано иное список инициализации. Так что да, конструктор будет вызван.

вы можете проверить это для себя либо:

  • наличие конструктора для печати сообщения (которое вы увидите) или
  • сделать конструктор частным или объявить конструктор с аргументами, чтобы конструктор по умолчанию больше не генерируется автоматически (компилятор откажется от компиляции, так как он больше не может по умолчанию-построить объект aggregate)

Если вы вызываете методы или члены доступа к объекту, который не был инициализирован или который уже был разрушен (оба сценария можно спроектировать), то у вас есть неопределенное поведение.


A. конструктор для вашего класса myClass_A будет вызываться в момент создания экземпляра объекта класса myClass_B.

B. Вы не можете вызывать методы неинициализированных объектов, например

myClass_A *pObj;
pObj->myFunction_A();

выдаст вам исключение.

C. Вы никогда не можете вызвать метод успешно без полного построения объекта.