首先看一个题目:
1 一个类一般要提供3个构造函数,默认构造函数; 拷贝构造函数; 赋值函数(operator=重载)。
如果不定义这3个函数,编译器也会生成缺省的默认构造函数; 拷贝构造函数。( 赋值函数不太确定是否会生成缺省的)。
2 拷贝构造函数和赋值函数,缺省情况下,实行位拷贝。
类中可能含有指向某内存空间的指针,是定义这两个函数的主要原因。你需要决定是否要求指针指向同一空间,或者再次分配内存。
3 使用默认构造函数时,必须不能使用括号。
例如s1, s4。s1显然是调用c1。s4呢?s4是声明了一个返回值为A的函数。
4 拷贝构造函数和赋值函数的区别,拷贝构造函数在建立对象的时候使用; 而赋值函数在赋值时引用(废话:))
例如s2是调用c2。s3是调用c4。如果他们中任何一个没定义,那么相应的调用就会使用位拷贝.
可能s2中的语法令人和s3混淆,s5中的语法就相当清晰了。
5 如果类中定义了包含某类型T的参数的构造函数,则就有了T到类的类型转换。
例如s6。5是int型的,类A有这样的参数的构造函数,那么先将5利用相应的构造函数( A(int) )转化为A的对象,或者说是生成一个A的对象。等号左边,显然是调用默认构造函数生成一个类的对象。最后调用赋值函数,完成赋值!
6 还得谈一下编译器的区别。
A b()
{
A a;
return a;
}
A c=b();
在G++下,上面的代码是不调用拷贝构造函数或默认构造函数的。
而使用VC下,是调用拷贝构造函数的。
我个人也是认为应该调用拷贝构造函数,首先是由局部变量生成返回的临时变量,其次是由返回值构造新的对象。需要调用两次。但这里编译器G++做了优化。
如果在拷贝函数中改变了某个成员变量i的值,那么上面的代码是不会成功的。
比如i默认构造为0, 拷贝构造函数把它修改为2, 然后经过上面的代码,在g++下c.i仍然是0而不是2。
实验代码如下:
平台
g++ version 4.0.3
Ubuntu 6.06 LTS - the Dapper Drake - released in June 2006
#include <cstdio>
//using namespace std;
/*
拷贝构造函数和=号重载的作用在于
防止位拷贝对类中指针,既而内存使用的影响
析构函数要合理的释放内存
=号重载在VC中是要求必须有返回值的。
=号重载记得要判断是否是自赋值。
=号重载最好是返回A&。这样就提高了效率。
*/
class A
{
//除const static的变量可以在类体内赋值外,其余变量均不可以赋值
int i;
public:
//默认构造函数
A()
{
i=0;
printf("A/n");
}
//拷贝构造函数
//拷贝构造函数的作用主要在于:如果类中含有指针,可以决定
// 该指针是否需要指向新的内存空间
// 否则,位拷贝的话只是完成简单的复制。
A(const A&)
{
i=1;
printf("AConst/n");
}
//一般构造函数
A(int i)
{
i=2;
printf("AInt/n");
}
//=号重载
//返回值如果是A,则如果有return语句,则会调用拷贝构造函数
// A& 则不会
A& operator=(const A&)
{
i=3;
printf("A=/n");
//
return *this;
/*
A a;
//此时也不调用拷贝构造函数, 编译器优化了?
//答案应该是肯定的。VC编译器中没有对此作优化,仍然需要调用
return a;
*/
}
void speak()
{
printf("i = %d/n",i);
}
static void CSpeak()
{
printf("I'm the class!/n");
}
};
int main (int argc, char * argv[])
{
printf("Test 1: /n");
A a;
a.speak();
printf("/n");
printf("Test 2: /n");
//先调用A(int),利用参数5构造一个A对象
// 或说成将5强制类型转换为A
//然后调用默认构造函数构造一个A对象
//再然后利用重载运算符赋值
A()=5;
printf("/n");
printf("Test 3: /n");
//不是声明对象,而是声明函数
A b();
b().speak();;
printf("/n");
printf("Test 4: /n");
A * c=new A();
printf("/n");
printf("Test 5: /n");
A d;
A e;
d=e;//如果不重载运算符,不会调用拷贝构造函数
d.speak();
e.speak();
printf("/n");
printf("Test 6: /n");
A f;
A g=f;//此时调用拷贝构造函数
printf("/n");
return 1;
}
A b()
{
/*
A a;
a.speak();
//不调用拷贝构造函数,VC编译器中没有对此作优化,仍然需要调用
return a;
*/
//调用拷贝构造函数
return (*(new A()));
}
有一个类
class A
{
public:
A(); //c1
A(const A&); //c2
A(int i); //c3
A& operator=(const A&); //c4
};
问如下语句都调用哪些构造函数?
A a; //s1
A b=a; //s2
b=a; //s3
A c(); //s4
A b(a); //s5
A()=5; //s6
先讲一下收获:
class A
{
public:
A(); //c1
A(const A&); //c2
A(int i); //c3
A& operator=(const A&); //c4
};
问如下语句都调用哪些构造函数?
A a; //s1
A b=a; //s2
b=a; //s3
A c(); //s4
A b(a); //s5
A()=5; //s6
1 一个类一般要提供3个构造函数,默认构造函数; 拷贝构造函数; 赋值函数(operator=重载)。
如果不定义这3个函数,编译器也会生成缺省的默认构造函数; 拷贝构造函数。( 赋值函数不太确定是否会生成缺省的)。
2 拷贝构造函数和赋值函数,缺省情况下,实行位拷贝。
类中可能含有指向某内存空间的指针,是定义这两个函数的主要原因。你需要决定是否要求指针指向同一空间,或者再次分配内存。
3 使用默认构造函数时,必须不能使用括号。
例如s1, s4。s1显然是调用c1。s4呢?s4是声明了一个返回值为A的函数。
4 拷贝构造函数和赋值函数的区别,拷贝构造函数在建立对象的时候使用; 而赋值函数在赋值时引用(废话:))
例如s2是调用c2。s3是调用c4。如果他们中任何一个没定义,那么相应的调用就会使用位拷贝.
可能s2中的语法令人和s3混淆,s5中的语法就相当清晰了。
5 如果类中定义了包含某类型T的参数的构造函数,则就有了T到类的类型转换。
例如s6。5是int型的,类A有这样的参数的构造函数,那么先将5利用相应的构造函数( A(int) )转化为A的对象,或者说是生成一个A的对象。等号左边,显然是调用默认构造函数生成一个类的对象。最后调用赋值函数,完成赋值!
6 还得谈一下编译器的区别。
A b()
{
A a;
return a;
}
A c=b();
在G++下,上面的代码是不调用拷贝构造函数或默认构造函数的。
而使用VC下,是调用拷贝构造函数的。
我个人也是认为应该调用拷贝构造函数,首先是由局部变量生成返回的临时变量,其次是由返回值构造新的对象。需要调用两次。但这里编译器G++做了优化。
如果在拷贝函数中改变了某个成员变量i的值,那么上面的代码是不会成功的。
比如i默认构造为0, 拷贝构造函数把它修改为2, 然后经过上面的代码,在g++下c.i仍然是0而不是2。
实验代码如下:
平台
g++ version 4.0.3
Ubuntu 6.06 LTS - the Dapper Drake - released in June 2006
#include <cstdio>
//using namespace std;
/*
拷贝构造函数和=号重载的作用在于
防止位拷贝对类中指针,既而内存使用的影响
析构函数要合理的释放内存
=号重载在VC中是要求必须有返回值的。
=号重载记得要判断是否是自赋值。
=号重载最好是返回A&。这样就提高了效率。
*/
class A
{
//除const static的变量可以在类体内赋值外,其余变量均不可以赋值
int i;
public:
//默认构造函数
A()
{
i=0;
printf("A/n");
}
//拷贝构造函数
//拷贝构造函数的作用主要在于:如果类中含有指针,可以决定
// 该指针是否需要指向新的内存空间
// 否则,位拷贝的话只是完成简单的复制。
A(const A&)
{
i=1;
printf("AConst/n");
}
//一般构造函数
A(int i)
{
i=2;
printf("AInt/n");
}
//=号重载
//返回值如果是A,则如果有return语句,则会调用拷贝构造函数
// A& 则不会
A& operator=(const A&)
{
i=3;
printf("A=/n");
//
return *this;
/*
A a;
//此时也不调用拷贝构造函数, 编译器优化了?
//答案应该是肯定的。VC编译器中没有对此作优化,仍然需要调用
return a;
*/
}
void speak()
{
printf("i = %d/n",i);
}
static void CSpeak()
{
printf("I'm the class!/n");
}
};
int main (int argc, char * argv[])
{
printf("Test 1: /n");
A a;
a.speak();
printf("/n");
printf("Test 2: /n");
//先调用A(int),利用参数5构造一个A对象
// 或说成将5强制类型转换为A
//然后调用默认构造函数构造一个A对象
//再然后利用重载运算符赋值
A()=5;
printf("/n");
printf("Test 3: /n");
//不是声明对象,而是声明函数
A b();
b().speak();;
printf("/n");
printf("Test 4: /n");
A * c=new A();
printf("/n");
printf("Test 5: /n");
A d;
A e;
d=e;//如果不重载运算符,不会调用拷贝构造函数
d.speak();
e.speak();
printf("/n");
printf("Test 6: /n");
A f;
A g=f;//此时调用拷贝构造函数
printf("/n");
return 1;
}
A b()
{
/*
A a;
a.speak();
//不调用拷贝构造函数,VC编译器中没有对此作优化,仍然需要调用
return a;
*/
//调用拷贝构造函数
return (*(new A()));
}