Что делает двоеточие после имени конструктора 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 ответов
Это список инициализации, который является частью реализации конструктора.
Подпись конструктора:
MyClass();
Это означает, что конструктор может быть вызван без параметров. Это делает его конструктором по умолчанию, то есть тем, который будет вызываться по умолчанию при написании MyClass someObject;
,
Часть : m_classID(-1), m_userdata(0)
называется списком инициализации. Это способ инициализировать некоторые поля вашего объекта (все они, если хотите) значениями по вашему выбору, вместо того, чтобы оставлять их как неопределенные.
После выполнения списка инициализации выполняется тело конструктора (которое в вашем примере оказывается пустым). Внутри него вы можете делать больше назначений, но как только вы введете его, все поля уже были инициализированы - либо для случайных, неуказанных значений, либо для тех, которые вы выбрали в своем списке инициализации. Это означает, что присвоения, которые вы делаете в теле конструктора, будут не инициализацией, а изменением значений.
Это список инициализации.
К тому времени, как вы попадете в тело конструктора, все поля уже созданы; если они имеют конструкторы по умолчанию, они уже были вызваны. Теперь, если вы присваиваете им значение в теле конструктора, вы вызываете оператор присваивания копии, что может означать освобождение и повторный запрос ресурсов (например, памяти), если объект имеет их.
Так что в случае примитивных типов, таких как int, нет никакого преимущества по сравнению с назначением их в теле конструктора. В случае объектов, которые имеют конструктор, это оптимизация производительности, потому что она избегает прохождения двух инициализаций объекта вместо одной.
Список инициализации необходим, если одно из полей является ссылкой, потому что ссылка никогда не может быть нулевой, даже в течение короткого времени между созданием объекта и телом конструктора. Следующее вызывает ошибку C2758: "MyClass::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 параметрами, которые имеют значения по умолчанию. Однако значения не имеют типов и не должны компилироваться вообще.
Это называется списком инициализации члена. Он используется для вызова констант суперкласса и предоставления вашим переменным-членам начального значения в момент их создания.
В этом случае он инициализируется m_classID
до -1 и m_userData
в NULL.
Это не совсем эквивалентно присваиванию в теле конструктора, поскольку последний сначала создает переменные-члены, а затем присваивает им. При инициализации начальное значение предоставляется во время создания, поэтому в случае сложных объектов оно может быть более эффективным.
Это не точно оператор. Это часть синтаксиса для конструктора.
Это говорит о том, что после него будет список переменных-членов и их начальных значений.
Постоянные члены должны быть инициализированы таким образом. Здесь также можно инициализировать неконстанты, если это можно сделать с помощью одного выражения. Если для инициализации члена требуется больше кода, чем нужно, вы должны поместить реальный код между {} для этого.
Многим людям нравится помещать почти весь их код конструктора в список инициализаторов. У меня есть один сотрудник, который регулярно пишет классы с несколькими экранами инициализаторов, а затем помещает "{}" для кода конструктора.
Это список инициализации. В вашем примере это скорее что-то вроде этого (что-то вроде этого - не значит, что оно эквивалентно во всех случаях):
class MyClass {
public:
MyClass(){
m_classID = -1;
m_userdata = 0;
}
int m_classID;
void *m_userdata;
};
Это начало списка инициализатора, который устанавливает переменные-члены во время создания объекта. Ваш пример "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 эквивалентно, потому что речь идет только о примитивных типах.
Если члены являются классами (структурами), то вы должны предпочесть список инициализации. Это потому, что в противном случае объекты создаются по умолчанию и затем назначаются.