C++拾遗--构造函数(一)默认构造
前言
对一个类而言,构造函数恐怕是最重要的一个成员函数了。关于构造函数的细节繁多,并且随着新标准的提出,构造函数有了新的特性。本文来集中探讨下构造函数的那些鲜为人知的一面。
构造函数
构造函数的作用众所周知:在类的对象被创建时,控制对象的初始化和赋值。
构造函数的一般形式:
类名(arg_list);
其中arg_list是用逗号隔开的参数列表。
特点:无返回值类型,且不可加const限制。
默认构造函数
需要特别指出,无参的构造函数是默认的,有参但都有默认参数的构造函数也是默认构造。
举例证明:
#include <iostream>
using namespace std;
class MyClass
{
protected:
int a = 0;
int b{1};
public:
MyClass(int a = 0, int b = 0):a(a), b(b) //有默认实参的构造方法就是默认构造
{
cout << "MyClass(int a = 0, int b = 0)" << endl;
}
int getA()const
{
return a;
}
int getB()const
{
return b;
}
};
int main()
{
MyClass my; //调用默认构造
cout << "a = " << my.getA() << ends << "b = " << my.getB() << endl;
cin.get();
return 0;
}
运行
从运行结果,即可验证结论。有参但都有默认参数的构造函数也是默认构造,这一点恐怕是很多人都容易忽略的、
默认构造初始化类的数据成员的规则:
- 如果存在类内的初始值,用它来初始化成员。
- 否则,默认初始化。(这个规则虽是标准,但大多数编译器还做不到)
几个概念
1.类内初始值。
在上例代码中,int a = 0; int b{1};这种形式就是类内初始值。后面会说明何时使用类内初始值。
2.默认初始化。
内置类型,如int,double类型等。int->0,double->0.0。就是默认初始化。
类类型,使用该类的默认构造初始化,就是默认初始化。
默认构造并不是总是存在的,若有另一个构造函数(非默认构造)存在,则编译器并不会提供一个无参的默认构造。新标准提出了一种方式:如 MyClass() = default; 关键字 default 的这种使用,表明要求编译器生成一个默认的构造函数。
3.初始值列表
构造函数 MyClass(int a = 0, int b = 0):a(a), b(b){...} 其中 a(a), b(b)就是初始值列表。
4.什么是初始化?什么是赋值?
对象在内存中创建(有了内存实体),第一次有的值,叫做初始化。把原先的值覆盖掉,叫做赋值。
一般情况下,构造函数在初始值列表中完成初始化,在函数体中完成赋值。
这个结论并不好证明,但还是有办法的,办法就是借助:const类型和引用类型。如下代码:
#include <iostream>
using namespace std;
class MyClass
{
protected:
int a = 0;
const int ca = 0;
int& ra;
public:
MyClass(int a) :a(a), ca(a), ra(this->a){}
int getA()const
{
return a;
}
void printCa()const
{
cout << "ca = " << ca << endl;
}
void printRa()const
{
cout << "ra = " << ra << endl;
}
};
int main()
{
MyClass my(1);
cout << my.getA() << endl;
my.printCa();
my.printRa();
cin.get();
return 0;
}
运行
const类型和引用类型,在创建时,必须进行初始化。若是把 ca = a; ra = this->a; 移到构造函数体内部,则无法通过编译。也就是说,一旦进入构造函数体,初始化就已经完成了。
需要指出,初始值列表的顺序并不代表着实际初始变量的顺序,而成员的声明顺序才是。
本专栏目录
所有内容的目录