【C++类和对象】初始化列表与隐式类型转换

简介: 【C++类和对象】初始化列表与隐式类型转换

💞💞 前言

hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹

1.初始化列表

1.1初始化列表定义

C++中的初始化列表是一种在对象构造函数中初始化成员变量的方法。它通过在构造函数的参数列表后面使用冒号来指定成员变量的初始化值,而不是在构造函数体内使用赋值语句来初始化。

在构造函数体内使用赋值语句来初始化:

class Date
{
public:
 Date(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
private:
 int _year;
 int _month;
 int _day;
};

上述例子中我们使用构造函数对类的成员变量进行赋值;

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。

初始化列表:

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

class Date
{
public:
  Date(int year, int month, int day)
  : _year(year)
  , _month(month)
  , _day(day)
  {}

private:
  int _year;
  int _month;
  int _day;
};


在上面的例子中,构造函数的初始化列表使用冒号 “:” 来指定成员变量 _year_month_day的初始值。在构造函数体内,不需要再使用赋值语句来初始化这些成员变量。


使用初始化列表可以提高代码执行效率,特别是在成员变量为对象类型时,可以避免多次调用默认构造函数和拷贝构造函数。

1.2初始化列表原因

在C++类和对象中有些成员变量必须定义的时候初始化,这时候如果只是简单的使用构造函数来赋值是不可行的,所以C++引入了初始化列表这个概念;

类中包含以下成员,必须放在初始化列表位置进行初始化:

1.引用成员变量

2.const成员变量

3.自定义类型成员(且该类没有默认构造函数时)

class A
{
public:
  A(int a)
   :_a(a)
   {}
private:
  int _a;
};
class B
{
public:
   B(int a, int ref)
   :_aobj(a)
   ,_ref(ref)
   ,_n(10)
   {}
private:
   A _aobj; // 没有默认构造函数
   int& _ref; // 引用
   const int _n; // const 
};


  • 在上面的例子中,常量成员变量 _n必须在构造函数的初始化列表中进行初始化,因为常量成员变量在对象创建后不能修改;
  • 引用成员变量 _ref 必须在构造函数的初始化列表中进行初始化,因为引用成员变量在创建后不能修改绑定的对象。
  • 没有默认构造函数的类类型成员变量_aobj:如果成员变量是一个类类型的对象,并且该类没有默认构造函数(无参构造函数),则必须在初始化列表中调用该类的有参构造函数进行初始化。

1.3初始化列表注意点

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:

引用成员变量

const成员变量

自定义类型成员(且该类没有默认构造函数时)

  1. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
    例如:
class Time
{
public:
  Time(int hour = 0)
  :_hour(hour)
  {
  cout << "Time()" << endl;
  }
private:
  int _hour;
};
class Date
{
public:
  Date(int day)
  {}
private:
  int _day;
  Time _t;
};
int main()
{
  Date d(1);
}

结果如下:

通过上述例子我们发现初始化列表是每个成员定义的地方,不管你写不写,每个成员都要走初始化列表,如果没写,对于内置类型给随机值,对于自定义类型会去调它的默认构造,走它自己的初始化列表;

  1. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关;

例如:

class A
{
public:
   A(int a)
  :_a1(a)
   ,_a2(_a1)
   {}
 
   void Print()
   {
   cout<<_a1<<" "<<_a2<<endl;
   }
private:
   int _a2;
   int _a1;
};
int main()
{
  A aa(1);
  aa.Print();
}

结果如下:

上述例子说明初始化列表的顺序是按照成员变量声明的顺序来的,首先先初始化_a2,这时_a1还没初始化是随机值,赋给_a2,所以_a2打印出来是随机值;然后再按照声明顺序初始化_a1为1;所以最终结果是1和随机值。

2. explicit关键字

2.1explicit关键字定义

在编程语言中,explicit是一个关键字,用于修饰构造函数,在对象初始化时明确地指定对象的类型,以防止隐式类型转换。使用explicit关键字可以禁止隐式转换,并要求使用显式的类型转换。

2.2隐式类型转换

我们先来了解一下什么是隐式类型转换?

隐式类型转换是指在表达式中,当操作数的类型与预期类型不匹配时,编译器自动将其转换为目标类型的过程。这种转换是自动进行的,不需要程序员显式地进行类型转换操作。

例如,当一个整数类型的值与一个浮点数类型的值进行运算时,编译器会自动将整数类型转换为浮点数类型,以便进行运算。

隐式类型转换可以方便地进行一些常见的类型转换,但也可能导致潜在的错误和不一致性。因此,在进行隐式类型转换时,程序员需要注意类型的兼容性和可能的风险。有时候,显式地进行类型转换会更加安全和清晰。


2.3explicit关键字使用

构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用。

接收单个参数的构造函数具体表现:

  1. 构造函数只有一个参数
  2. 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
  3. 全缺省构造函数

例如:

class Date
{
public:
  // 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用  
   Date(int year)
    :_year(year)
  {}
private:
  int _year;
  int _month;
  int _day;
};


void Test()
{
  Date d1(2022);
  // 用一个整形变量给日期类型对象赋值
  // 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
  d1 = 2023;
}

上述例子中单参构造函数,没有使用explicit修饰,具有类型转换作用,一旦使用了explicit修饰,就会编译不通过,例如:

class Date
{
public:
  // 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用  
  explicit Date(int year)
    :_year(year)
  {}
private:
  int _year;
  int _month;
  int _day;
};

结果如下:

同样对于构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值以及全缺省构造函数都具有类型转换的作用,一旦使用explicit修饰,就会禁止类型转换。

3.结语

初始化列表是C++类和对象中初始化成员变量的方式,在一些情况下可以提高效率和代码可读性。隐式类型转换在某些情况下可以方便地进行类型转换,但有时也会导致意外的结果或者不可预测的行为,所以C++提供了explicit来禁止隐式类型转换。

以上就是初始化列表与隐式类型转换所有的内容啦~ 完结撒花 ~🥳🎉🎉

相关文章
|
5天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
25 5
|
11天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
40 4
|
12天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
36 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
24 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++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
53 1
|
1月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
20 1