这节不讲述构造函数的一般用法和概念。
1. 构造函数的类型转换功能
单参数构造函数的类型转换功能是指,如果类定义中提供了只带一个参数(没有其他参数或其他参数都有默认值)的构造函数,则当一个其他类型的数值或变量x赋值(注意不是初始化)给该类的对象A时,由于类型不一致,系统自动以x为实参调用该类的单参数构造函数,创建一个该类的临时对象,并将临时对象赋给A,实现隐式类型转换。
如下:
//类Money的定义
class Money{
int yuan;
int jiao;
int fen;
public:
Money(double d);
Money(int yuan, int jiao, int fen);
Money() { cout << "调用默认的构造函数" << endl; };
};
构造函数如下:
Money::Money(double d)//隐式类型传换的构造函数
{
yuan = (int)d;
jiao = (int)(d * 10) % 10;
fen = (int)(d * 100) % 10;
cout << yuan << "元" << jiao << "角" << fen << "分" << endl;
}
Money::Money(int yuan, int jiao, int fen)
{
this->yuan = yuan;
this->jiao = jiao;
this->fen = fen;
cout << this->yuan << "元" << this->jiao << "角" << this->fen << "分" << endl;
}
主函数调用方法:
int main()
{
Money m1=3.14;//这是初始化对象,所以编译器将其转换为Money m1(3.14),所以不是类型转换
Money m2(1, 3, 5);
Money m3;//会首先调用空的构造函数
m3 = 4.12;//此时调用类型转换功能的构造函数
return 0;
}
所以调试结果如下:
总结:当采用先定义对象,然后对对象进行赋值时,采用类型转换功能的构造函数会被调用,也就是说此时对象会调用两次不同的构造函数。
2. 拷贝构造函数
拷贝构造函数的形参是本类的对象的引用。其作用是使用一个已经存在的对象去初始化一个新的同类的对象
形式:
ClassName::ClassName(ClassName &c)
{
......//函数体完成对应数据成员的赋值
}
实例如下:
//类的定义
class Money{
int yuan;
int jiao;
int fen;
public:
Money(double d);
Money(int yuan, int jiao, int fen);
Money() { cout << "调用默认的构造函数" << endl; };
Money(Money &c);
void print();
int Getyuan();
int Getjiao();
int Getfen();
};
拷贝构造函数的定义:
Money::Money(Money & c)
{
yuan = c.yuan;
jiao = c.jiao;
fen = c.fen;
cout << "调用拷贝构造函数" << endl;
}
两个函数:
Money Max(Money& a, Money& b)
{
if (a.Getyuan() > b.Getyuan())
return a;
else if(a.Getyuan()<b.Getyuan())
return b;
else
{
if (a.Getjiao() > b.Getjiao())
return a;
else if (a.Getjiao() < b.Getjiao())
return b;
else
{
if (a.Getfen() > b.Getfen())
return a;
else if (a.Getfen() < b.Getfen())
return b;
else
cerr << "两个数值相同" << endl;
return 0;
}
}
}
Money Min(Money a, Money b)
{
if (a.Getyuan() < b.Getyuan())
return a;
else if (a.Getyuan()>b.Getyuan())
return b;
else
{
if (a.Getjiao() < b.Getjiao())
return a;
else if (a.Getjiao() > b.Getjiao())
return b;
else
{
if (a.Getfen() < b.Getfen())
return a;
else if (a.Getfen() > b.Getfen())
return b;
else
cerr << "两个数值相同" << endl;
return 0;
}
}
}
函数中可以看出区别在于形参中一个使用的是类的别名,一个使用的是类的对象
主函数调用:
int main()
{
Money m1=1.35;
Money m2(1, 3, 6);
//Money m3;
//m3 = 4.12;
//Money m4 = m3;
//m4.print();
Money max;
max=Max(m1, m2);
cout<<"钱最大为:"<<endl;
max.print();
Money min;
min = Min(m1, m2);
cout << "钱最小为:" << endl;
min.print();
return 0;
}
调试结果如下:
拷贝构造函数调用情况:
当用类的一个对象去初始化另一个对象时;
当调用一个含有类的对象参数的函数时;
函数的返回值是类的对象时;
系统会自动生成一个默认的拷贝构造函数,功能是把初始值对象的每个数据成员的值都赋给新建立的对象。
注意:对于类中包含指向动态内存的指针的情况,若使用默认的拷贝构造函数复制对象会带来严重的问题。析构的时候会将同一片堆析构两次。
3. 对象成员和构造函数
一个类的对象可以作为另一个类的数据成员,此时把该对象成为类的对象成员。
注意:
当一个类中出现对象成员时,该类的构造函数就要为对象成员初始化,必须在构造函数的初始化列表中完成。
若未在该类的构造函数初始化列表中给出对象成员的初始化项,则调用对象成员的默认构造函数
若包含多个对象成员,对象成员的构造函数的调用顺序由他们在该类中的说明顺序有关,而与初始化表中的顺序无关
初始化对象成员时必须用对象成员名,而不是类型名。
若一个类中含有多个对象成员,则可在该类的初始化列表中列出多个对象成员的初始化项,以逗号隔开。
若一个类包含对象成员,建立该类对象时,先调用对象成员的构造函数,初始化相应的对象成员,才执行构造函数,初始化该类的其他数据成员。
实例如下:
void my_strcmp(char *a, char const *b);
class Money{
int yuan;
int jiao;
int fen;
public:
Money(double d);
Money(int yuan, int jiao, int fen);
Money() { cout << "调用默认的构造函数" << endl; };
Money(Money &c);
void print();
int Getyuan();
int Getjiao();
int Getfen();
};
class Person {
Money money;
char name[10];
int age;
public:
Person(char const *name, int age) :money(3.14)
{
my_strcmp(this->name,name);
this->age = age;
};
void print();
};
代码中类Person中包含了类Money。调用如下:
int main()
{
Person p1("liming",14);
p1.print();
return 0;
}
void Person::print()
{
cout << name << endl;
}
调试结果如下:
可以看出其首先调用的是Money的构造函数,然后调用Person类的,同时先初始化对象成员
将程序中的Person构造函数改为
Person(char const *name, int age)
{
my_strcmp(this->name,name);
this->age = age;
cout << "调用Person的构造函数" << endl;
};
也就是不初始化对象成员,则先调用对象成员的默认构造函数,然后调用Person的构造函数。