Что делает двоеточие после имени конструктора C++? [дубликат]


этот вопрос уже есть ответ здесь:

что делает оператор двоеточия ( " :") в этом конструкторе? Это эквивалентно MyClass(m_classID = -1, m_userdata = 0);?

class MyClass {
public:

    MyClass() : m_classID(-1), m_userdata(0) { 
    }

    int m_classID;
    void *m_userdata;
};
9 77

9 ответов:

Это список инициализации, и является частью реализации конструктора.

подпись конструктора:

MyClass();

Это означает, что конструктор может быть вызван без параметров. Это делает его конструктор по умолчанию, т. е. тот, который будет вызываться по умолчанию, когда вы пишите MyClass someObject;.

часть : m_classID(-1), m_userdata(0) называется список инициализации. Это способ инициализации некоторых полей ваш объект (все они, если хотите) со значениями по вашему выбору, вместо того, чтобы оставлять их неопределенными.

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

Это список инициализации.

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

Так что в случае примитивных типов, таких как int, нет никакого преимущества по сравнению с назначением их в теле конструктора. В случае объектов, имеющих конструктор, это оптимизация производительности, поскольку она позволяет избежать прохождения двух инициализаций объекта вместо одной.

список инициализации необходим, если одно из полей является ссылкой, потому что ссылка никогда не может быть нулевой, даже в течение короткого времени между построением объекта и телом конструктора. Следующее вызывает ошибку C2758: 'MyClass:: member_': должно быть инициализируется в списке инициализаторов конструктора base/member

class MyClass {
public :
    MyClass(std::string& arg) {
        member_ = arg;
    }
    std::string& member_;
};

единственный правильный путь:

class MyClass {
public :
    MyClass(std::string& arg) 
        : member_(arg) 
    {
    }
    std::string& member_;
};

он обозначает начало списка инициализаторов, который предназначен для инициализации переменных-членов вашего объекта.

как: MyClass(m_classID = -1, m_userdata = 0);

это объявляет конструктор, который может принимать аргументы (так что я мог бы создать MyClass С помощью MyClass m = MyClass(3, 4), в результате m_classID быть 3, а m_userdata быть 4). Если бы я не передавал никаких аргументов MyClass конструктор, это приведет к созданию эквивалентного объекта для версии со списком инициализаторов.

он сигнализирует о начале списка инициализаторов.

также это не эквивалентно MyClass (m_classId=-1,m_userData=0). Это попытка определить конструктор с 2 параметрами, которые имеют значения по умолчанию. Однако значения не имеют типов, и он не должен компилироваться вообще.

Это список инициализации. В вашем примере, это скорее что-то вроде этого (что-то вроде этого - не означает, что это эквивалентно во всех случаях):


class MyClass {

public:

    MyClass(){
         m_classID = -1;
         m_userdata = 0;
    }

    int m_classID;
    void *m_userdata;

};

это называется список инициализации членов. Он используется для вызова суперкласса constrctors и дает вашим переменным-членам начальное значение во время их создания.

в данном случае это инициализация m_classID -1 и m_userData к нулю.

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

Это не совсем оператор. Это часть синтаксиса конструктора.

Он говорит, что после него будет список переменных-членов и их начальных значений.

постоянные члены должны быть инициализированы таким образом. Здесь также можно инициализировать непостоянные значения, если это можно сделать с помощью одного выражения. Если для инициализации члена требуется больше кода, Вы должны поместить фактический код между {}, Чтобы сделать это.

A многие люди любят помещать почти весь свой код конструктора в список инициализаторов. У меня есть один сотрудник, который регулярно пишет классы с несколькими экранами инициализаторов, а затем ставит "{}" для кода конструктора.

это начало списка инициализаторов, который устанавливает переменные-члены во время построения объекта. Ваш пример " MyClass (m_classID = -1, m_userdata = 0);" невозможен, поскольку вы не определили правильный конструктор, и вы все равно не сможете получить доступ к переменным-членам в списке параметров... вы могли бы иметь что-то вроде:

MyClass( int classId = -1, void* userData = 0 ) : m_classID(classId), m_userdata(userData) {}

список инициализаторов считается лучше, чем:

MyClass( int classId = -1, void* userData = 0 ) {
    m_classID = classId;
    m_userdata = userData;
}

Google для получения дополнительной информации.

в этом случае: да, ist эквивалентен, потому что речь идет только о примитивных типах.

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