初学C++,遇到了这样一个问题,代码如下:
#include <iostream>
using namespace std;
class Complex {
float real, imag;
public:
Complex(float r = 0.0, float i = 0.0):real(r), imag(i) {
cout << "Complex constructor called" << endl;
}
Complex (Complex& complex):real(complex.real), imag(complex.imag) {
cout << "Complex copy constructor called" << endl;
}
int main() {
Complex c3 = 3.14;
}
运行的时候会有编译错误,如下:
foobar.cpp:42:13: error: no viable constructor copying variable of
type 'Complex'
Complex c3 = 3.14;
^ ~~~~
foobar.cpp:10:5: note: candidate constructor not viable: expects an
l-value for 1st argument
Complex (Complex& complex):real(complex.real), imag(comp...
^
然而,当我删掉我显式定义的拷贝构造函数后,就可以正常编译了,即:
#include <iostream>
using namespace std;
class Complex {
float real, imag;
public:
Complex(float r = 0.0, float i = 0.0):real(r), imag(i) {
cout << "Complex constructor called" << endl;
}
int main() {
Complex c3 = 3.14;
}
这样就可以正常编译,请问这是为什么呢?
我用的是 g++ 4.2.1
目前我知道的是:当执行"Complex c3 = 3.14"时发生这些:先调用我显式定义的默认构造函数"Complex(float r = 3.14, float i = 0.0)"创建了一个临时对象,然后利用拷贝构造函数(如果有合法的)去创建c3,然后讲临时对象析构。
其实我的问题有两个:
1)为什么默认的拷贝构函数造能通过编译
2)为什么我定义的拷贝构函数造不能通过编译
补充:之前的问题已解决,但是现在遇到了新的问题
代码如下:
#include <iostream>
using namespace std;
class Complex {
float real, imag;
public:
Complex(float r = 0.0, float i = 0.0):real(r), imag(i) {
cout << "Complex constructor called" << endl;
}
Complex(const Complex& complex):real(complex.real), imag(complex.imag) {
cout << "Complex copy constructor called" << endl;
}
};
int main() {
Complex c = 3.22;
}
可以通过编译,但是输出结果为:
Complex constructor called
并没有调用拷贝构造函数啊,为什么呢?是被编译器优化了吗?
如果不需要调用拷贝构造函数,那为什么之前不加const限定的拷贝构造函数不能通过编译呢?
我用的编译器是 g++ 4.2.1
PHPz2017-04-17 15:01:27
因為C++規格允許編譯器在這個時候最佳化掉不必要的拷貝構造。
GCC加上參數-fno-elide-constructors可以不最佳化。
雖然這個呼叫拷貝構造的過程是後來被最佳化掉了的。但是編譯檢查語法的時候仍然需要它存在的。而VC則是直接優化掉,根本不會呼叫拷貝建構函式。
優化了以後,Complex c3 = 3.14;這樣的初始化表達式和Complex c3(3.14);是等價的了。
天蓬老师2017-04-17 15:01:27
Complex c3=3.14;
這個拷貝構造的是常數
所以你應該將定義改為Complex (const Complex& complex);
加上const關鍵字後,非常量的變數也可以拷貝構造,所以一般的拷貝構造函數都加上const。
希望對你有幫助!
不好意思!我先前的回答有誤
對於Complex c3=3.14;
因為這個3.14不是complex對象,系統應該將其認為float類型對象,所以其調用將相當於:Complex c3(3.14);
呼叫的是你定義的建構子,而這個過程這是初始化,沒有呼叫到預設的拷貝建構子。
而拷貝建構函式呼叫的時機是拷貝相同類別的物件的時候,也就是當拷貝的是Complex物件時才呼叫。
例如:Complex c4=c3;
這時才會呼叫拷貝建構子。
我將你之前的程式碼在我的電腦上運行時沒有發現編譯錯誤,所以我估計是編譯器的原因,對於這方面我也不熟悉所以無法為你解答。
希望我的回答對你有幫助!
迷茫2017-04-17 15:01:27
如果你是貼程式碼的時候忘了花括號和分號的話,這是 clang 的 bug 吧…
用 Visual C++ 14.0 (VS2015) 可以編譯通過的。
#include <iostream>
using namespace std;
class Complex
{
float real, imag;
public:
Complex(float r = 0.0, float i = 0.0) : real(r), imag(i)
{
cout << "Complex constructor called" << endl;
}
Complex(const Complex& complex) : real(complex.real), imag(complex.imag) // 有没有 const 都可以
{
cout << "Complex copy constructor called" << endl;
}
friend ostream& operator<<(ostream& os, const Complex& one)
{
os << "<" << one.real << ", " << one.imag << ">";
return os;
}
};
int main()
{
Complex c3 = 3.14; // 怎么搞都可以
Complex c4{1}; // 怎么搞都可以
Complex c5{1, 2}; // 怎么搞都可以
cout << "c3: " << c3 << endl;
cout << "c4: " << c4 << endl;
cout << "c5: " << c5 << endl;
return 0;
}
運行結果:
Complex constructor called
Complex constructor called
Complex constructor called
c3: <3.14, 0>
c4: <1, 0>
c5: <1, 2>
然後對於你加的問題:
Complex c3 = 3.14;
做的確是初始化 initialization,而非賦值 assignment。解釋在 C++ 之父 Bjarne Stroustrup 的著作 A Tour of C++ 中:
C++ offers a variety of notations for expressing initialization, such as the = used above, and a universal form based on curly-brace-delimited initializer lists:
double d1 = 2.3; // initialize d1 to 2.3
double d2 {2.3}; // initialize d2 to 2.3
complex<double> z = 1; // a complex number with double-precision floating-point scalars
complex<double> z2 {d1,d2};
complex<double> z3 = {1,2}; // the = is optional with { ... }
vector<int> v {1,2,3,4,5,6}; // a vector of ints
The = form is traditional and dates back to C, but if in doubt, use the general {}-list form.