我想知道以下两个课程有什么区别。
示例1:
class A
{
string name;
public:
A(const char* _name):name(_name){}
void print(){cout<<"A's name:"<<name<<endl;}
};
示例2:
class A
{
string name;
public:
A(const char* _name){name(_name);}
void print(){cout<<"A's name:"<<name<<endl;}}
为什么传递示例1而最后一个错误? 感谢
答案 0 :(得分:3)
第一个示例是实际初始化。它具有许多优点,包括成为const
成员的唯一方法,并具有适当的异常安全性。
第二个例子不是有效的C ++,AFAIK。如果您改为编写name = name_
,那么这只是正常的分配。当然,这并不总是可行的;对象可能是const
,或者没有定义赋值运算符。这种方法的效率也可能低于第一个示例,因为该对象都是默认初始化的和。
至于为什么初始化列表在构造函数体之前;好吧,这就是语言的定义方式。
答案 1 :(得分:3)
在示例1中,您立即使用给定值初始化字符串。 在示例2中,您首先创建一个空字符串,然后再将其分配。
尽管存在一些性能差异,并且由于复制构造函数处理等原因而忽略了可能的差异,但它基本上是相同的结果。
但是,一旦您使用const
成员,您将不得不使用示例1的方式来执行此操作,例如我通常通过以下方式创建唯一ID:
class SomeObject
{
static unsigned int nextID = 0;
const unsigned int ID;
SomeObject() : ID(nextID++)
{
// you can't change ID here anymore due to it being const
}
}
答案 2 :(得分:2)
这就是语言的定义方式。成员初始值设定项应放在构造函数的主体之前。
答案 3 :(得分:1)
在第一个示例中,使用ctr获取char *作为参数初始化成员名称。
在第二种情况下,它首先使用默认ctr进行初始化,然后由赋值运算符(operator =)获取值。这就是为什么你的情况已经构造错误,所以你不能再次使用ctr你只能使用赋值运算符。
答案 4 :(得分:0)
原因是名称查找在初始化列表和函数体中的工作方式不同:
class A
{
std::string foo; // member name
public:
A(const char* foo) // argument name
: foo(foo) // member foo, followed by argument foo.
{
std::cout << foo; // argument foo.
}
};
如果初始化列表在函数体内,则成员foo
和参数foo
之间会有歧义。
答案 5 :(得分:0)
初始化列表背后的动机是由于const字段按值保存对象(而不是引用/指针字段)。
这些字段必须初始化一次(由于它们的常量)。如果C ++没有初始化列表,那么ctor看起来就像:
class A {
public:
const string s;
const string t;
A() {
// Access to either s or t is not allowed - they weren't initialized
s = "some-string";
// now you can access s but you can't access t
f(*this);
t = "some other string";
// From now you can access both ...
}
}
void f(A& a) {
// Can you access a.s or a.t?
cout << a.s << a.t;
}
如果没有初始化列表,ctor可以将类型为A
的部分初始化对象传递给函数,并且该函数无法知道哪些字段已初始化。编译器/链接器检查风险太大且非常困难。