看代码,关于拷贝构造函数

ccnyou 2012-07-13 04:11:36

#include <IOSTREAM>
using namespace std;

class A
{
public:
explicit A(const char c)
{
cout<<"构造函数"<<endl;
}

A(const A& a)
{
cout<<"拷贝构造函数"<<endl;
}
};

int main()
{
char c = 0;

A a = c; //这里编译错误:cannot convert from 'char' to 'class A',
//A a(c);正常,为什么?
return 0;
}



去掉构造函数中的explicit能编译通过。但是这个错误我就不明白了,怎么会有'char' to 'class A'的转换发生?
A a = c;中,到底是调用构造函数,还是拷贝构造函数呢?
...全文
2185 43 打赏 收藏 转发到动态 举报
写回复
用AI写文章
43 条回复
切换为时间正序
请发表友善的回复…
发表回复
wm_hust 2012-07-22
  • 打赏
  • 举报
回复
纠正:

刚才的解答有问题,这里更正一下。

A a = c; 相当于

A a = A temp(c);

是隐式地调用A(const char c),但该构造函数已申明为显示(explicit )的了,
所以报错。

在不去掉explicit 修饰的情况下,可以直接显示调用构造函数A a = A temp(c);

这里跟operator= 是有关系的,A a = c; 本质上是一个赋值语句,但先要构造一个 A 对象实例才能进行赋值。
所以系统就隐式地调用A(const char c)来临时构造一个A 对象实例,但该构造函数不允许隐式调用,所以才会报错。

wm_hust 2012-07-22
  • 打赏
  • 举报
回复
正解:

A a = c; 相当于


A a = A(c);
问题出在 A(c)这里,因为
explicit A(const char c)
{
cout<<"构造函数"<<endl;
}
的意思就是不允许象 A(c) 这样调用构造函数,所以报错,跟operator= 没什么关系。
东东0615 2012-07-20
  • 打赏
  • 举报
回复
这里如果不用explicit,调用构造函数是确定的,上面有的人说:先构造了一个临时对象temp,然后a=temp调用了拷贝构造函数,就不对了吧?我调试过,跟本就没有调用拷贝构造函数。
swat_01 2012-07-19
  • 打赏
  • 举报
回复
去掉explicit就可以,否则char无法隐式转换为A,参考《深入探讨C++对象模型》第三章 构造函数语义学
  • 打赏
  • 举报
回复
无论如何,是否优化。我想,那个带char型参数的构造函数的调用是没跑了。
你加上explicit是错就很正常了。
LJT25 2012-07-19
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 的回复:]
楼上说的不太对哦。
1.explicit 这个是用来指定构造函数不用来进行隐式类型转换的
2. A a = XXX这么写是会调用拷贝构造函数的。
所以正确的理解是,当你不写explicit时,编译器判断 A a = c;这句,A的拷贝构造函数是需要一个A的对象的引用的,所以进行了隐式类型转换,把c给了A的构造函数,构造出了一个临时的A对象,并将这个对象以引用的方式传入了拷贝构造函数。最后成功……
[/Quote]
这个说的对。
BlueRomantic 2012-07-19
  • 打赏
  • 举报
回复
[Quote=引用楼主 的回复:]
C/C++ code

#include <IOSTREAM>
using namespace std;

class A
{
public:
explicit A(const char c)
{
cout<<"构造函数"<<endl;
}

A(const A& a)
{
cout<<"拷贝构造函数"<<endl;……
[/Quote]
调用的是构造函数。首先对于拷贝构造函数的核心点在于“构造”二字,无论前面加了多少修饰词。而调用拷贝构造函数,那么即为将一个已经存在的class A的实例,通过引用传递给你想要新构造的class。而代码中的A a = c;这个c是class A的实例吗?不是,它只是一个char类型。所有,这里不是调用拷贝构造函数。那么为什么这里是调用构造函数呢?而加上了explicit关键字修饰符就不行了呢?在C++ ISO Standard的12.3.1&12.3.2其实已经写明了,如果一个类的构造函数没有加上explicit修饰符,那么该构造函数可以通过单独的一个参数而被调用,而这个参数必须符合构造函数的第一个参数的类型,然后完成类型转化(不要忘记class其实也只是一个类型而已),而这种的构造函数被称为converting constructor。下面的例子是C++ ISO Standard文档列出来的,希望对你有帮助。

class X {
// ...
public:
X(int);
X(const char*, int =0);
};
void f(X arg)
{
X a = 1; // a = X(1)
X b = "Jessie"; // b = X("Jessie",0)
a = 2; // a = X(2)
f(3); // f(X(3))
}

这一个代码比较简单,我相信你应该能看懂。另外附上C++ ISO Standard英文原文说明:
A constructor declared without the function-specifier explicit that can be called with a single parameter specifies a conversion from the type of its first parameter to the type of its class. Such a constructor is called a converting constructor.
而加上explicit后,则防止了这种类型的隐式转换。如C++ ISO Standard英文原文说明:
An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax or where casts are explicitly used
P.S.这是我的第一次回帖,有点长,希望对楼主有所帮助。我也只是一个大四刚毕业,即将读研的学生,所有肯定有所不恰当的地方,欢迎交流!
  • 打赏
  • 举报
回复
其实可以从另外一个简单的例子来理解这个


int a ;
double b;
a = b;

上面这个过程,b要先隐式的类型转换为int,才能参与=的运算,由此,A a = c的过程是,c要先隐式转换为A的类型,explicit的作用很明显了,不用多说了。。
yujie_v 2012-07-18
  • 打赏
  • 举报
回复
这个目的不同的,看默认函数
彭家老三 2012-07-18
  • 打赏
  • 举报
回复
以下是引用网页中的内容:
按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象,如下面所示:
class String {
String ( const char* p ); // 用C风格的字符串p作为初始化值
//…
}
String s1 = “hello”; //OK 隐式转换,等价于String s1 = String(“hello”);

但是有的时候可能会不需要这种隐式转换,如下:
class String {
String ( int n ); //本意是预先分配n个字节给字符串
String ( const char* p ); // 用C风格的字符串p作为初始化值
//…
}

下面两种写法比较正常:
String s2 ( 10 ); //OK 分配10个字节的空字符串
String s3 = String ( 10 ); //OK 分配10个字节的空字符串

下面两种写法就比较疑惑了:
String s4 = 10; //编译通过,也是分配10个字节的空字符串
String s5 = ‘a’; //编译通过,分配int(‘a’)个字节的空字符串

s4 和s5 分别把一个int型和char型,隐式转换成了分配若干字节的空字符串,容易令人误解。
为了避免这种错误的发生,我们可以声明显示的转换,使用explicit 关键字:
class String {
explicit String ( int n ); //本意是预先分配n个字节给字符串
String ( const char* p ); // 用C风格的字符串p作为初始化值
//…
}
加上explicit,就抑制了String ( int n )的隐式转换,

下面两种写法仍然正确:
String s2 ( 10 ); //OK 分配10个字节的空字符串
String s3 = String ( 10 ); //OK 分配10个字节的空字符串

下面两种写法就不允许了:
String s4 = 10; //编译不通过,不允许隐式的转换
String s5 = ‘a’; //编译不通过,不允许隐式的转换

因此,某些时候,explicit 可以有效得防止构造函数的隐式转换带来的错误或者误解

----------------------------------------------------------
explicit 只对构造函数起作用,用来抑制隐式转换。如:
class A {
A(int a);
};
int Function(A a);

当调用 Function(2) 的时候,2 会隐式转换为 A 类型。这种情况常常不是程序员想要的结果,所以,要避免之,就可以这样写:

class A {
explicit A(int a);
};
int Function(A a);

这样,当调用 Function(2) 的时候,编译器会给出错误信息(除非 Function 有个以 int 为参数的重载形式),这就避免了在程序员毫不知情的情况下出现错误。

总结:explicit 只对构造函数起作用,用来抑制隐式转换。

参考:
http://blog.csdn.net/smilelance/archive/2007/03/14/1528737.aspx
http://topic.csdn.net/t/20040509/15/3046021.html

lvjl2008 2012-07-18
  • 打赏
  • 举报
回复
A a=c 是在吧字符类型变量c赋值给A类型的对象a,当然编译器会报错,而A a(c)则是调用那个接受字符类型变量的构造函数。
DBFNO 2012-07-17
  • 打赏
  • 举报
回复
因为explicit抑制由构造函数定义的隐式转换,来防止在需要隐式转换的上下文中使用构造函数。

因此当你定义 char c 来为 A a赋值时,explicit阻止了将 char c转换为class A类型,因此就出错了

如果楼主想要赋值,可以显示的使用构造函数
例如:
A a = A(c);
这样就行了。
yrnaaa 2012-07-14
  • 打赏
  • 举报
回复
explicit就是防止 A a = c; 这种赋值发生的。
A a(c);正常,为什么?==>因为这是调用了拷贝构造函数。
machongma 2012-07-14
  • 打赏
  • 举报
回复
#include <iostream>
using namespace std;
class A
{
private:

char i;

public:
A(const char c)
{
cout<<"构造函数"<<endl;
}
A()
{
}


~A()
{
cout<<"析构函数"<<endl;
}





};
f(A a)
{
cout<<"拷贝构造函数"<<endl;
}
int main()
{
/*char c = 0;

A a = c; //这里编译错误:cannot convert from 'char' to 'class A',
//A a(c);正常,为什么?*/

char c=0;


A a(c);
A b=f(a);


return 0;
}
machongma 2012-07-14
  • 打赏
  • 举报
回复
#include <iostream>
using namespace std;
class A
{
private:

char i;

public:
A(const char c)
{
cout<<"构造函数"<<endl;
}
A()
{
}


~A()
{
cout<<"析构函数"<<endl;
}





};
f(A a)
{
cout<<"拷贝构造函数"<<endl;
}
int main()
{
/*char c = 0;

A a = c; //这里编译错误:cannot convert from 'char' to 'class A',
//A a(c);正常,为什么?*/

char c=0;


A a(c);
A b=f(a);


return 0;
}
ccnyou 2012-07-13
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]

A a = c 就是 A a(c)。绝对不会跟operator = 扯上关系,以前没有,现在没有,将来也不会有。

这个叫隐式调用构造函数,就是虽然看起来是个=,但其实就是调用的构造函数,所以叫隐式。
explicit英文的意思就是显示,你在构造函数前加一个这个东西就是告诉编译器禁止隐式调用构造函数。编译器当然报错了。
[/Quote]
精辟独到,受教了
ccnyou 2012-07-13
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 的回复:]

好多人说的差不多都对了,LZ看看《inside the c++ object model》 或者《effective C++》《more effec C++》吧!
[/Quote]
《effective C++》刚到手,就是看这本书才让我用 explicit 的
zjwdmlmx 2012-07-13
  • 打赏
  • 举报
回复
好多人说的差不多都对了,LZ看看《inside the c++ object model》 或者《effective C++》《more effec C++》吧!
assassin5616 2012-07-13
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]

A a = c 就是 A a(c)。绝对不会跟operator = 扯上关系,以前没有,现在没有,将来也不会有。

这个叫隐式调用构造函数,就是虽然看起来是个=,但其实就是调用的构造函数,所以叫隐式。
explicit英文的意思就是显示,你在构造函数前加一个这个东西就是告诉编译器禁止隐式调用构造函数。编译器当然报错了。
[/Quote]
有个很简单的办法来验证A a = c不会调用operator =.你可以重写operator =。打点日志什么的。然后你可以这么写代码
A b('c');
A a = b;
你会发现拷贝构造函数会被调用而不是operator =。所以在定义一个变量的时候使用的=总是跟构造有关。跟operator =没有关系。
assassin5616 2012-07-13
  • 打赏
  • 举报
回复
A a = c 就是 A a(c)。绝对不会跟operator = 扯上关系,以前没有,现在没有,将来也不会有。

这个叫隐式调用构造函数,就是虽然看起来是个=,但其实就是调用的构造函数,所以叫隐式。
explicit英文的意思就是显示,你在构造函数前加一个这个东西就是告诉编译器禁止隐式调用构造函数。编译器当然报错了。
加载更多回复(23)

64,661

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧