C++ 学习12(23.10.27)隐式转换和explicit

151 阅读5分钟

1 :C++中的隐式转换和explicit关键字

1.1 隐式转换

C++中的隐式转换是指在某些情况下,编译器会自动将一种数据类型转换为另一种数据类型,而无需显式地使用类型转换运算符。

以下是一些常见的隐式转换情况:

  1. 整数类型之间的转换。例如,将一个short类型的变量赋值给一个int类型的变量,或者将一个char类型的变量与一个int类型的变量进行运算。

  2. 浮点类型之间的转换。例如,将一个float类型的变量赋值给一个double类型的变量。

  3. 数组类型和指针类型之间的转换。例如,将一个数组名作为参数传递给函数时,它会自动转换为一个指向数组第一个元素的指针。

  4. 派生类和基类之间的转换。例如,将一个派生类的对象赋值给一个基类的引用或指针时,编译器会自动进行向上转型。

需要注意的是,虽然隐式转换可以方便我们的编程,但有时也可能会带来一些意想不到的问题。因此,在编写代码时应该尽量避免过多地依赖隐式转换,以确保程序的正确性和可读性。

在64位电脑上,常见的数据类型的字节数、比特数和表示范围如下所示:

  1. 整数类型:

    • int:4字节(32位),32比特,范围为-2,147,483,648到2,147,483,647。
    • short:2字节(16位),16比特,范围为-32,768到32,767。
    • long:8字节(64位),64比特,范围为-9,223,372,036,854,775,808到9,223,372,036,854,775,807。
    • long long:8字节(64位),64比特,范围为-9,223,372,036,854,775,808到9,223,372,036,854,775,807。
  2. 浮点类型:

    • float:4字节(32位),32比特,范围为约±1.18E-38到±3.4E+38,精度约为6位小数。
    • double:8字节(64位),64比特,范围为约±2.23E-308到±1.8E+308,精度约为15位小数。
    • long double:8字节(64位),64比特,范围和精度与double相同,但可能提供更高的精度。
  3. 字符类型:

    • char:1字节(8位),8比特,通常用于表示ASCII字符,范围为-128到127或0到255(取决于是否有符号)。

常见的数据类型:

隐式转换:

  1. 数字类型之间的隐式转换:

    • 较小的整数类型可以隐式转换为较大的整数类型,例如将short转换为int,int转换为long。
    • 整数类型可以隐式转换为浮点类型,例如将int转换为float,long转换为double。
    • **浮点类型可以隐式转换为更高精度的浮点类型,**例如将float转换为double。
  2. 字符类型之间的隐式转换:

    • 字符类型可以隐式转换为整数类型,例如将char转换为int
  3. 枚举类型之间的隐式转换:

    • **不同枚举类型之间的值可以相互隐式转换,**但建议使用显式转换以避免潜在的错误。
  4. 指针类型之间的隐式转换:

    • 具有继承关系的类指针之间可以进行隐式转换,例如将派生类指针转换为基类指针。
    • void指针可以隐式转换为任意类型的指针,但需要注意在转换后正确处理类型。

需要注意的是,隐式转换可能会导致精度损失或数据截断,因此在进行隐式转换时应谨慎,并确保不会引发意外的错误

1.2 隐式构造函数

在C++中,隐式构造函数(Implicit Constructor)指的是可以通过单个参数调用的构造函数,用于将该参数类型转换为类的对象。隐式构造函数允许在某些情况下,通过隐式地进行类型转换来创建对象

1 :单参数构造函数:如果一个类具有只接受单个参数的构造函数,那么在需要该类对象的地方,可以使用该参数类型进行隐式构造。

构造函数初始化列表知识补充:

ClassName::ClassName(parameters) : member1(value1), member2(value2), ... {
    // 构造函数的其他代码
}

其中: ClassName 是类的名称。 parameters 是构造函数的参数列表。 member1(value1), member2(value2), ... 是初始化列表,用于对类的成员变量进行初始化。每个成员变量都以逗号分隔,格式为成员变量名(初始值)。 在初始化列表中,可以使用传递给构造函数的参数或其他常量、表达式等来初始化成员变量。

以下是一个示例

class MyClass {
public:
    MyClass(int value1, int value2) : m_member1(value1), m_member2(value2) {
        // 构造函数的其他
[Something went wrong, please try again later.]

关于隐式构造函数:

#include <iostream>
using namespace std;

class MyInt{
public:
   MyInt(int value):m_value(value){}//初始化列表
   int getValue() const {return m_value;} //return 里面一定记得带封号

private:
   int m_value;
};

void printMyInt(MyInt myint){
   cout << "MyInt value" << myint.getValue() << endl;
}

int main(){
   printMyInt(10); // 隐式构造MyInt对象
   return 0;
}

代码解析:

1:定义了一个名为MyInt的类,具有一个接受单个int类型参数的构造函数和一个返回成员变量值的成员函数getValue()。 

2:定义了一个名为printMyInt的函数,接受一个MyInt对象作为参数,并打印出该对象的值。 3:在main()函数中,通过调用printMyInt()函数并传递整数值10作为参数,隐式地构造了一个 MyInt对象,并将其传递给printMyInt()函数进行打印。 

4:程序最后返回0,表示正常退出。

int getValue() const {return m_value;}

注意:该函数返回一个整数类型的值,表示存储在MyInt对象中的整数值。由于该函数不会修改任何成员变量的值,因此使用了const关键字来修饰函数,以保证该函数不会对对象的状态进行修改。其中,const关键字位于函数声明的末尾,表示该函数是一个常量成员函数。在函数体内,我们直接返回成员变量m_value的值,以便从外部访问该值。

结果:

1.3 explicit 关键字

在C++中,explicit是一个关键字,用于修饰单参数构造函数。当构造函数被声明为explicit时,它将禁止隐式的类型转换。 通常情况下,如果一个类的构造函数只有一个参数,那么可以使用该构造函数进行隐式的类型转换。但是,如果将构造函数声明为explicit,则编译器将不会自动执行隐式的类型转换,而要求显式地调用构造函数。

#include <iostream>

class MyInt{
public:
   explicit MyInt(int value):m_value(value){
   //构造函数其他代码
   }

   int getValue()const{
      return m_value;
   }

private:
   int m_value;
};

void printValue(const MyInt& myint){
   int value = myint.getValue();
   std::cout << "Value:" << value << std::endl;
}

int main(){
   MyInt myint1(10);//直接调用构造函数进行初始化
   printValue(myint1);//调用printValue函数,隐式转换成MyInt类型

   //MyInt myint2 = 20;//错误,禁止使用隐式转换
   //printValue(30);//禁止使用隐式转换

   return 0;
}

printValue(20): 是使用隐式构造函数;

printValue(myint1); 是隐式将myint1 隐式转换成MyInt类型。