目录
1. 隐式类型转换与关键字explicit:
1.1 隐式类型转换举例:
1.2 explicit关键字:
2. 友元:
2.1 友元函数:
2.2 友元类:
3. 内部类:
4. 勘误:
1. 隐式类型转换与关键字explicit:
1.1 隐式类型转换举例:
首先列举一个最基本的隐式类型转换:
int i = 0;
double d = i;
在上述给出的代码中,隐式类型转换发生在第二行,对于转换的具体过程需要注意,变量并不是直接就转换类型为,而是变量会首先转换成一个具有常属性的中间变量,中间变量再进行一次转换,转换为变量。对于上述代码,如果将去掉,并不会直接影响代码的运行。但是,如果对上述代码进行更改:
int j = 0;
double& b = j;
运行代码,编译器报错如下所示:
这是因为,引用,即&所引用的并不是,而是在转换类型时,中途产生的具有常属性的中间变量。在加上后,代码可以正常运行:
int j = 0;
const double& b = j;
上面所举出的例子都是针对内置内型而言,对于自定义类型,同样存在着隐式类型转换,例如对于下方给出的类:
class A
{
public:
A( int i)
:_a(0) {};
public:
int _a = 0;
};
通过这个类来建立两个对象:
int main()
{
Test1();
A d(1);
A d1 = 3;
return 0;
}
第一种方法是常规的用于建立一个对象的方法,而第二种方法,在之前的文章中并没有进行过应用,因为可以看作用一个类型来初始化一个自定义类型。这个过程便涉及了内置类型和自定义类型的隐式类型转换,其大体过程可以分为两个步骤,第一个步骤,内置类型通过一次构造转换成一个类型的中间变量,这个中间变量再通过一次拷贝构造来转换为变量。
需要注意,对于上述内置类型与自定义类型的隐式类型转换需要条件:类中存在单参数的构造函数
(注:并不只是单参数构造函数或者说这个定义并不绝对,在文章后段会对这个条件进行补充)
同理,如果将上述代码进行更改为引用的形式,也需要加上,即:
const A& d2 = 3;
对于用于隐式类型转换的单参数构造函数,他的参数类型决定了可以被转换的参数类型,例如想让一个类型的变量转为自定义类型,则构造函数应该改为:
class A
{
public:
explicit A( int* year)
:_a(0) {};
public:
int _a = 0;
};
上面的注释中提到,内置类型与自定义类型发生隐式类型转换的条件是存在单参数构造函数这一定义并不绝对,例如,将上方代码的构造函数进行更改:
class A
{
public:
A( int year, int month = 1, int day = 10)
:_a(0) {};
public:
int _a = 0;
};
可以看到,此时的构造函数有三各参数,但是其中两个参数都带有缺省值。
利用下方的代码建立对象:
A d1 = (2024, 1, 10);
运行代码,编译器没有给出报错。可见,上面说的单参数并不是只有一个参数,对于多参数,如果只有一个参数需要进行传值,其他参数均有缺省值也可以进行。
同样,假如构造函数的参数是全缺省,即:
class A
{
public:
A( int year = 2024, int month = 1, int day = 10)
:_a(0) {};
public:
int _a = 0;
};
此时代码依旧可以正常运行。
为了更清晰的了解上述隐式类型转换的结果,给出下列代码并运行:
class A
{
public:
A(int year = 2024, int month = 1, int day = 10)
: _year(year)
, _month(month)
, _day(day)
{};
public:
int _year;
int _month;
int _day;
};
int main()
{
A d1 = (2024, 1, 10);
return 0;
}
运行上述代码,通过监视区来查看自定义类型,即:
产生这样结果的原因是在创建对象时,用于初始化的内容为:
(2024, 1, 10);
由逗号操作符的性质可以知道,上述内容的结果为最右端的数字,也就是说,对于创建对象的那行代码,可以改写为下面的形式:
A d1 = 10;
因此,产生了结果中第一个数字,而后面的,则是由构造函数的缺省参数来决定的
1.2 explicit关键字:
对于explicit关键字,其主要作用就是显示隐式类型转换的发生,其使用方法就是在构造函数的前面加上此关键字,即:
class A
{
public:
explicit A( int year = 2024, int month = 1, int day = 10)
:_a(0) {};
public:
int _a = 0;
};
2. 友元:
2.1 友元函数:
在之前关于流插入、流提取这两个运算符进行运算符重载的时候就提到过,由于这两个运算符在重载时,需要保证两个参数的顺序,因此,需要把此运算符重载作为全局函数而非成员函数,但是,成员函数在类中访问限定符为私有的情况下,全局函数不能访问成员变量,因此,需要采用友元的方式,让全局函数可以访问成员变量。
对于友元函数的具体使用方法可以在C++类与对象基础(6)-CSDN博客进行查看,这里不再重复叙述
2.2 友元类:
对于友元类,其大致的定义以及特点如下:
1.友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
2.友元关系是单向的,不具有交换性。
3.比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问 Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递
4.如果C是B的友元, B是A的友元,则不能说明C时A的友元。
5.友元关系不能继承,在继承位置再给大家详细介绍。
其使用方法与友元函数基本相同,这里也不进行过多叙述
3. 内部类:
内部类就是在一个类中再定义一个类,具体由下面的代码给出:
class A
{
public:
A(int a)
: _a(0)
{};
class B
{
public:
int _B;
};
private:
int _a;
};
内部类和其他的类没有很大的区别,可以看作一个普通的类,但是受到了类的类域以及访问限定符的限制。并且,对于一个内部类而言,就是外部类的一个友元类。
假如使用下面的代码来计算上面给出的代码的大小:
cout << sizeof(d1) << endl;
代码结果为:
可以看到,在计算类的大小时,不会计算内部类的大小。
4. 勘误:
由于个人能力有限,书中难免出现汉字拼写错误、代码意义解释错误、内容逻辑以及理解错误等不同类型的错误。首先感谢各位大佬能花掉自己宝贵的时间阅读此文章,愿大佬们斧正,发现错误可以通过私信联系,本人不胜感激。