我想知道下面两个类有什么不同
示例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是通过的,最后一个是错误的?由于
在示例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
}
}
第一个例子是一个实际的初始化。它有许多优点,包括作为设置const
成员的唯一方法,以及具有适当的异常安全性。
第二个例子在c++中是无效的。如果你写的是name = name_
,那么这就是一个正常的赋值。当然,这并不总是可能的;对象可以是const
,也可以没有定义赋值操作符。这种方法也可能比第一个示例效率低,因为对象是默认初始化的和分配的。
对于,为什么初始化列表在构造函数体之前;这就是语言被定义的方式。
语言就是这样定义的。成员初始化式应该放在构造函数体之前。
在第一个示例中,使用以char *为参数的ctr初始化成员名
在第二种情况下,首先使用默认的ctr初始化,然后通过赋值操作符(operator=)获取值。这就是为什么你的情况是错误的,它已经在那里构造了,所以你不能再次使用ctr,你可以使用赋值操作符。
原因是名称查找在初始化器列表和函数体中的工作方式不同:
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
之间存在歧义。
初始化列表背后的动机是由于const字段按值保存对象(与引用/指针字段相反)。
这样的字段必须精确初始化一次(由于它们的const-ness)。如果c++没有初始化列表,那么一个actor将看起来像这样:
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;
}
如果没有初始化列表,则可以将A
类型的部分初始化对象传递给函数,而该函数将无法知道哪些字段已初始化。风险太大,编译器/链接器很难检查。