对象数组是在C++学习中接触对象之初就学习到了的概念,但在一次实践中我发现一个奇怪的现象:
class A{
private:
int i;
int j;
public:
A();
A(int ii,int jj);
A(A& a);
~A();
};
A::A() {
cout<<"Constructor"<<endl;
}
A::~A() {
cout<<"Destructor"<<endl;
}
A::A(A &a) {
cout<<"Copy Constructor"<<endl;
}
A::A(int ii, int jj) {
cout<<"2 numbers constructor!"<<endl;
i = ii;
j = jj;
}
int main(){
A a1;
A a2(a1);
A a3 = a1;
A a4(1,2);
A a5[] = {A(1,2), A(3,4)};
}
在g++(Apple LLVM version 10.0.0 (clang-1000.10.44.4))下编译不通过,提示error: no matching constructor for initialization of 'A',A a5[] = {A(1,2), A(3,4)};,但奇怪的是,将复制构造函数注释掉后能够成功编译。
经过规范的书写,即将复制构造函数中的参数表中加上const:
A::A(const A &a) {
cout<<"Copy Constructor"<<endl;
}
在按对象数组形式初始化对象时就不会报错了,这引起了我对const关键字的一些思考:在《C/C++ const关键字的一些理解》一文中我给出了自己对const的理解,即const只能阻止用户直接通过变量名修改数据。
细看报错,看来编译器尝试过为参数表形式的初始化匹配对应的构造函数,但无功而返,对应的错误提示为“第一参数位置期望一个左值”。
note: candidate constructor not viable: expects an l-value for 1st argument
A::A(A &a) {
^
note: candidate constructor not viable: requires 0 arguments, but 1 was provided
A::A() {
^
note: candidate constructor not viable: requires 2 arguments, but 1 was provided
A::A(int ii, int jj) {
这引起了我的一些灵感,我尝试使用A a6 = A(1,2);
得到了一样的错误提示,这可能说明对象数组的初始化方式在内部实现时类似于A a6 = A(1,2);
;明明传入了2个参数,编译器似乎发现不了,我尝试改为A(1,2,3),这时报错就出现在A(1,2,3),而不是a6=处了。
这时我大概了解到A a6 = A(1,2);
的本质应该就是A a6(A(1,2));
,其实是调用到了A(int i, int j)这个构造函数,只是对象数组赋值时调用了复制构造函数时出错。
出错的根本原因是,右值 不可以绑定到 非 const 左值引用,A(1,2)相当于右值,参数表中如果不加const就会报错。
至于为什么?资料说c++中临时变量默认const属性,所以只能传给const的引用。规定右值不能绑定到非 const 限定的左值引用。
异常对象另说;如果是右值引用或const左值引用绑定的,那生存期延长为引用;否则到完全表达式结束销毁。还有默认初始化数组元素时延长到数组初始化结束。
这样看来,本以为const只能起到按引用传参不被修改的作用,现在看来并非如此,其在C++标准中也有一些奇怪的作用,看来一入C++还真的深似海哦!