【C++】类和对象④(再谈构造函数:初始化列表,隐式类型转换,缺省值

简介: C++中的隐式类型转换在变量赋值和函数调用中常见,如`double`转`int`。取引用时,须用`const`以防修改临时变量,如`const int& b = a;`。类可以有隐式单参构造,使`A aa2 = 1;`合法,但`explicit`关键字可阻止这种转换。C++11起,成员变量可设默认值,如`int _b1 = 1;`。博客探讨构造函数、初始化列表及编译器优化,关注更多C++特性。


隐式类型转换
之前我们讲过,不同类型的内置类型变量在相互赋值时会有隐式类型转换。

double a = 10.5;
int b = a;

就如上面这个简单的赋值,在a赋值给b之前,会产生一个临时变量,最终赋给b值的就是这个临时变量。

当将不同类型的变量取引用时,需要加const的原因,是因为临时变量具有常性。

double a = 10.5;
// int& b = a;// 报错
// int& c = 10;// 报错
const int& b = a;// 正常运行
const int& c = 10;// 正常运行

上述代码中b取的就是a产生的临时变量的引用,临时变量存储在内存的静态区,具有常性,就跟第四行代码的数字10性质是一样的,当你加上const时,这种引用权限就被放开了,因为const确保了你不会对静态区的变量做出改动。对于C++的自定义类型,与内置类型遵循的规则是一样的。

C++支持一种类型转换式的构造:

class A
{
public:
A(int a)
:_a1(a)
{}
A(const A& aa)
:_a1(aa._a1)
{
cout << "A(const A& aa)" << endl;
}
private:
int _a1;
int _a2;
};
int main()
{
A aa1(1);
A aa2 = 1;
return 0;
}

对于main函数第一行代码是标准的调用了构造函数。而第二行,作为内置类型的1,竟然能给对象的初始化赋值,这是因为在赋值之前,产生了隐式类型转换,1作为一个参数传递给了构造函数,从而产生了一个临时对象,最终临时变量拷贝构造给aa2。

在调用此代码的过程中,我们发现,并没有调用拷贝构造函数,这是因为通过编译器的优化,省去了拷贝构造这一过程,简单来说就是:

这时候看这两行能否运行的原因应该就不困难了:

// A& ref = 10;// 报错
const A& ref = 10;//可运行
// 这里ref引用的是类型转换中用10构造的临时对象
在上面代码中,我们使用的构造函数一直是单参数的,可以使用特殊的隐式类型转换构造。但是如果构造函数是多参数的,该怎么使用类似于A aa = 1;的方式创建对象呢?其实C++提供了解决方案,那就是多参数构造。

class A
{
public:
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{}
A(const A& aa)
:_a1(aa._a1)
,_a2(aa._a2)
{}
void print()const
{
cout << _a1 << " " << _a2 << endl;
}
private:
int _a1;
int _a2;
};
// 多参数构造
int main()
{
A aa1 = { 2,2 };
aa1.print();
// A& ref = { 2,3 };//报错
const A& ref = { 2,3 };
ref.print();
return 0;
}

explicit关键字
这个知识点稍稍提一下,如果不想允许构造时出现类的隐式类型转换,可以在拷贝构造前加个explicit关键字,就可以成功限制类的隐式类型转换了。

关于explicit的更多使用,在后面有机会还会讲。

成员变量缺省值
之前讲过,在C++11的新标准中,支持为类中的成员变量提供缺省值。在类和对象中,提供的缺省值是提供给初始化列表使用的。由于支持隐式类型转换构造等原因,提供的缺省值可以非常灵活,见代码:

class A
{
public:
A(int a1)
:_a1(a1)
{
cout << "A(int a1)" << endl;
}
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{
cout << "A(int a1, int a2)" << endl;
}
A(const A& aa)
:_a1(aa._a1)
, _a2(aa._a2)
{
cout << "A(const A& aa)" << endl;
}
private:
int _a1;
int _a2;
};
class B
{
public:
private:
int _b1 = 1;// 缺省值可以给整型变量
int ptr = (int)malloc(40);// 可以开空间给指针
A _aa1 = 1;// 可以给对象类型(A _aa1(1);这样构造是错误的)
A _aa2 = { 1,2 };// 多参数构造
A _aa3 = _aa2;// 拷贝构造,缺省参数甚至可以是一个对象
};
int main()
{
B bb1;
return 0;
}

这些缺省参数,最终都会提供给初始化列表。

结语
本篇博客将最后两个默认成员函数做了一个收尾,再次谈到了构造函数的一些语法和特性,关于初始化列表的概念和使用;一种很新的创建对象方式,隐式类型转换方式创建对象,而explicit关键字可以限制这种转换的发生;最后还提到了C++11的新特性成员变量的缺省值,列出了对象,指针等类型给缺省值的方式。在类和对象的下一篇,会再介绍几个类和对象的小特性,以及编译器做出的优化。

博主还会继续产出有趣的内容,感谢大家的支持!♥

相关文章
|
10天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
37 4
|
11天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
35 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
1月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
23 3
|
1月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
53 1
|
1月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
19 1