作者不才,原文网址如下:
http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html
http://c.biancheng.net/cpp/biancheng/view/193.html
http://www.cnblogs.com/mr-wid/archive/2013/02/19/2917911.html
http://www.cnblogs.com/luxiaoxun/archive/2012/09/06/2673249.html
首先我们要明白关于构造函数的几个特点
1.在对象被创建时自动执行;
2.函数的函数名与类名相同;
3.没有返回值类型、也没有返回值;(意味着函数体中没有return语句)
4. 构造函数不能被显式调用。
然后,我们为什么要用到构造函数呢?
主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,而且一旦在类中定义了构造函数,那么创建对象时一定会被执行。
如果希望对不同的对象赋予不同的初始值,则需要使用带参数的构造函数,在调用不同对象的构造函数时,将不同的数据传给构造函数,以实现不同的初始化。
构造函数会产生什么效果呢?
1.给创建的对象一个标识符
2.为对象数据成员开辟内存空间
3.完成对象数据成员的初始化
好了,现在我们再来具体介绍构造函数
一,构造函数的初始化作用
class Counter
{
public:
Counter() // 类Counter的构造函数,特点:以类名作为函数名,无返回类型
{
m_value = 0;
}
private:
int m_value;//数据成员
}
该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数。
eg: Counter c1;
编译系统为对象c1的每个数据成员(m_value)分配内存空间,并调用构造函数Counter( )自动地初始化对象c1的m_value值设置为0(为何是0,下面有讲)
二,构造函数的种类
1.无参数构造函数
class Complex
{
private :
double m_real;
double m_imag;
public:
// 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做
// 若你写了其他种类的构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显式地写出来
Complex(void)
{
m_real = 0.0;
m_imag = 0.0;
}
}
e.g.:int main()
{
Complex c1,c2; // 调用了无参构造函数,数据成员初值被赋为0,0(解释了为何自动初始化后数值是0)
}
不带参数的构造函数使该类的每一个对象都得到相同的初始值。
2.一般构造函数(或称重载构造函数)
// 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)
// 例如:你还可以写一个 Complex( int num)的构造函数出来
Complex(double real, double imag)// // 创建对象时根据传入的参数不同调用不同的构造函数
{
m_real = real;
m_imag = imag;
}
e.g.:int main()
{
// 调用一般构造函数,数据成员初值被赋为指定值
Complex c3(1.0,2.5);
// 也可以使用下面的形式
Complex c3 = Complex(1.0,2.5);
}
3.复制构造函数(或称拷贝构造函数)
// 复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中
Complex(const Complex & c)
{
// 将对象c中的数据成员值复制过来
m_real = c.m_real;
m_imag = c.m_imag;
}
4.类型转换构造函数
其实就是一般的构造函数,对于出现这种单参数的构造函数,C++会默认将参数对应的类型转换为该类类型。
Complex(double r)//根据一个指定的类型的对象(double)创建一个本类的对象。
{
m_real = r;
m_imag = 0.0;
}
5.等号运算符重载(也叫赋值构造函数) 【注意和第三类的区别】
// 注意,这个类似复制构造函数,将等号右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建
// 若没有显示的写=运算符重载,则系统也会创建一个默认的等号运算符重载,只做一些基本的拷贝工作
Complex &operator=( const Complex &rhs )
{
if ( this == &rhs ) // 首先检测等号右边的是否就是左边的对象本身,若是本对象本身,则直接返回
{
return *this;
}
// 复制等号右边的成员到左边的对象中
this->m_real = rhs.m_real;
this->m_imag = rhs.m_imag;
return *this;
}
下面是原文中的一个例子,我摘了下来,感觉对理解上面的几个种类还是很有帮助的。
int main()
{
// 调用了无参构造函数,数据成员初值被赋为0.0
Complex c1,c2;
// 调用一般构造函数,数据成员初值被赋为指定值
Complex c3(1.0,2.5);
// 也可以使用下面的形式
Complex c3 = Complex(1.0,2.5);
c1 = c3; // 把c3的数据成员的值赋值给c1, 由于c1已经事先被创建,故此处不会调用任何构造函数,只会调用 = 号运算符重载函数
// 调用类型转换构造函数
c2 = 5.2; // 系统首先调用类型转换构造函数,将5.2创建为一个本类的临时对象,然后调用等号运算符重载,将该临时对象赋值给c2
// 调用拷贝构造函数( 有下面两种调用方式)
Complex c5(c2);
Complex c4 = c2; // 注意和 = 运算符重载区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2
//以我的理解,简单来说,重载中,两边的对象是已经建立好了的,而复制构造函数是根据一个已经存在的对象创造另一个新的对象
}
此处是另一个原文的小程序,按照这个程序的思路也会理解构造函数。
有两个长方柱,其长、宽、高分别为12, 20, 25和10, 14, 20,求它们的体积。编写一个基于对象的程序,在类中用带参数的构造函数。
#include <iostream>
using namespace std;
class Box
{
public :
Box(int,int,int);//带参数的构造函数
int volume( );
private :
int height;
int width;
int length;
};//注意这个格式,有个分号
Box::Box(int h,int w,int len) //在类外定义带参数的构造函数
{
height=h;
width=w;
length=len;
}
int Box::volume( ) //定义计算体积的函数
{
return (height*width*length);
}
int main( )
{
Box box1(12,25,30); //建立对象box1,并指定box1长、宽、高的值
//此处将12,25,30传递给构造函数,在构造函数中,将box1对象中的height,width,length初始化成相应的数值
cout<<"The volume of box1 is "<<box1.volume( )<<endl;
Box box2(15,30,21); //建立对象box2,并指定box2长、宽、高的值
cout<<"The volume of box2 is "<<box2.volume( )<<endl;
return 0;
}
程序运行结果如下:
The volume of box1 is 9000
The volume of box2 is 9450
三,构造函数的重载
与普通函数相同,构造函数也支持重载,但是我们也要避免出现二义性。
e.g.:
Point(int x = 0, int y = 0) //默认参数的构造函数
{
xPos = x;
yPos = y;
}
Point() //重载一个无参构造函数
{
xPos = 0;
yPos = 0;
}
当尝试用 Point 类重载一个无参数传入的对象 M 时,
Point M;
此时Point 函数具有二义性,这是因为 Point(int x = 0, int y = 0) 全部使用了默认参数,
即使我们不传入参数也不会出现错误, 但是在重载时又重载了一个不需要传入参数了构造函数 Point(),
这样就造成了当创建对象都不传入参数时编译器就不知道到底该使用哪个构造函数了, 就造成了二义性。
一个类可以提供多个构造函数,让用户在创建对象时进行选择,编译器会根据创建对象时传递的参数来确定调用哪一个构造函数。(避免二义性)
也就是说:
只有一个构造函数会被执行;
创建对象时提供的参数必须和其中的一个构造函数匹配,否则编译错误。
#include <iostream>
using namespace std;
class Student
{
private:
char *name;
int age;
float score;
public:
//声明构造函数
Student();
Student(char *, int, float);
//声明普通成员函数
void setname(char *);
void setage(int);
void setscore(float);
void say();
};
//定义构造函数
Student::Student(){}
Student::Student(char *name1, int age1, float score1)
{
name = name1;
age = age1;
score = score1;
}
//定义普通成员函数
void Student::setname(char *name1)
{
name = name1;
}
void Student::setage(int age1)
{
age = age1;
}
void Student::setscore(float score1)
{
score = score1;
}
void Student::say()
{
cout<<name<<"的年龄是 "<<age<<",成绩是 "<<score<<endl;
}
int main()
{
//创建对象时初始化成员变量
Student stu1("小明", 15, 90.5f);
stu1.say();
//调用成员函数来初始化成员变量的值
Student stu2;
stu2.setname("李磊");
stu2.setage(16);
stu2.setscore(95);
stu2.say();
return 0;
}
运行结果:
小明的年龄是 15,成绩是 90.5
李磊的年龄是 16,成绩是 95
类中定义了两个构造函数,一个带参数一个不带参数,它们是重载关系。当根据不带参数的构造函数创建对象时,不需要传参,成员变量不会被初始化,所以要调用成员函数来设置它们的值