C++类的自动转换和强制类型转换

文章目录

    • 类的自动转换和强制类型转换
      • 1. C++如何处理标准内置类型的转换?
        • 1. 初始化和赋值进行的转换
        • 2. 以{}的方式初始化时(列表初始化)进行的转换(C++11)
        • 3.表达式中的转换
        • 4. 传递参数时的转换
        • 5. 强制类型转换
      • 2. 类构造函数 将 内置类型 转换为 类对象 Stonewt(typeName)
        • 关键字explicit
      • 3. 转换函数 将 类对象 转换为 内置类型 operator typeName();
        • 总结
      • 4. 实现加法Stonewt + double时的选择

类的自动转换和强制类型转换

1. C++如何处理标准内置类型的转换?

C++内置的整型:unsigned long、long、unsigned int、int 、unsigned short、short、char、unsigned char、signed char和bool
C++新增的整型:unsigned long long和long long
C++内置的浮点类型:float、double和long double

在对不同的类型进行运算时,C++自动执行很多类型转换

  • 不同类型的变量进行的赋值运算时,C++将对值进行转换
  • 表达式中包含不同的类型时,C++将对值进行转换
  • 将参数传递给函数时,C++将对值进行转换

1. 初始化和赋值进行的转换

  1. 将一个值付赋值给取值范围更大的类型通常不会导致什么问题,如将short赋值给long变量并不会改变这个值,只是占用的字节更多而已。
  2. 然而,将一个很大的值,如long值(如211222333)赋值给float类型将降低精度,因为float只有6位有效数字,因此这个值江北四舍五入为2.11122E9.
转换 潜在问题
大浮点转小浮点:如double转换为float 精度降低(有效位数)
浮点型转换为整型 小数部分丢失,或者超出表示范围
大整型转小整型:如long转int 超出目标类型表示范围
float tree = 3;  //整数3自动转换为3.000000
int guess(3.9832); //dounle类型的数字被截取为int型的3
int debt = 7.2E12//超出了表示范围,不同的系统不一定得到个什么数

2. 以{}的方式初始化时(列表初始化)进行的转换(C++11)

使用花括号进行列表初始化时,类型转换的要求更严格,不允许缩窄(narrowing)。

const int code = 66;
int x= 66;
char c1 = {31325};  //缩窄了,char型放不下31325,不允许这种初始化
char c2 = {66}; //允许,没有缩窄,能放下66
char c3 = {code}; //允许,没有缩窄,能放下66
char c4 = {x};  //不允许,X不是个常数,不能放到花括号里
x = 31325;
char c5 = x; //不适用花括号时这种赋值是允许的,但超出了char的范围,结果不一定什么

3.表达式中的转换

同一个表达式包含两种不同的类型时,C++会进行两种自动转换:

  1. 一些类型在出现时变会被自动转换——(如bool,char,unsigned char,signed char 和 short的值被自动转换为int,也称整型提升
  2. 当运算设计两种类型时,较小的类型将被转换为较大的类型,如9.0/5 ,9.0默认是double,int型的5也将被转换为double型的5.0

4. 传递参数时的转换

传递参数时的类型通常由C++函数原型控制,然而,也可以取消原型对参数传递的控制。

5. 强制类型转换

两种写法:

(long) thron;   //传统C写法
long (thron)  //C++写法

第二种格式的设计使强制类型转换就像是函数调用,使内置类型的转换与用户为类设计的类型转换函数在形式上保持一致。

int a(9);       //括号法初始化
float b = 8;    //隐式转换法:int 8 ———— folat 8.0 ——— b,内置类型自动转换
double c = double(7); //显示转化法: int 7 ———— double 7.0 ———— c,强制类型转换

2. 类构造函数 将 内置类型 转换为 类对象 Stonewt(typeName)

C++语言不能自动转换不兼容的类型

int *p = 10;  //这句话是错误的,因为整型和指针完全不同

但是强转是可以用的
int *p  = (int*) 10; //这样是可行的,至于是否有意义是另外一回事

可以将构造函数用作自动类型转换函数,用来指示C++如何自动进行类型转换

Stonewt(double lbs);   // 这个构造函数用于将double类型的值转换为Stonewt类型
Stonewt(int stn, double lbs); // constructor for stone, lbs
Stonewt();       

Stonewt incognito = 275;   //隐式转换,在构造函数的指导下将 int 275 转换为 类对象
或者
Stonewt incognito(275);  //括号法初始化
或者
Stonewt incognito = Stonewt(275);  //显示调用构造函数,或者可以看做是进行强制类型转换,将int型的275强转为Stonewt类型的类对象incognito

只有接受一个参数的构造函数才能作为转换函数,下面的构造函数有两个参数,因此不能用来转换类型

Stonewt(int stn, double lbs);  //not a conversion function

然而,如果给第二个参数提供默认值,它便可以用于转换int

Stonewt(int stn, double lbs = 0);  //int to Stonewt conversion

关键字explicit

将构造函数用作自动类型转换函数这种特性并非总是合乎需要的,有时会导致以外的类型转换,C++新增了关键字explicit(显式的)用来关闭这种自动特性,可以这样声明构造函数:

explicit Stonewt(double); 

这将关闭上述示例中介绍的隐式转换,但是仍然允许显示转换,即显示强制类型转换

explicit Stonewt(double); 

Stonewt mycat;  //create a Stonewt object
mycat = 19.6;  错误,因为explicit关键字关闭了隐式转换
mycat = Stonewt(19.6);  //ok,仍然允许显示强制转换
mycat = (Stonewt) 19.6; //ok,这是C的写法的强转

遇到int 422,编译器查找Stonewt(int)进行转换,以便将int型变量转换为Stonewt型对象,由于没有提供这样的构造函数,编译器继续寻找接受其他内置类型的构造函数,发现Stonewt(double)满足这种要求,因此将int转换为double,然后使用Stonewt(double)将其转换为一个Stonewt对象

void display(const Stonewt& st, int n);

display(422,2); 
注意到这里第一个参数应该是Stonewt类型的对象,
但是实际传入的是int型的422,所以这里进行自动类型转换

3. 转换函数 将 类对象 转换为 内置类型 operator typeName();

特殊的C+运算符函数————转换函数:operator typeName();

  • 转换函数必须是类方法
  • 转换函数不能指定返回类型,但是也返回所需的值。(其实typeName就相当于返回类型了)
  • 转换函数不能有参数,通过类对象来调用,从而告知要转换的值,不需要参数。
class Stonewt
{
private:
        double pounds;
public:
       Stonewt(double lbs);

       operator int() const    //Stonewt 转换为 int 
       {
           return int (pounds + 0.5);  //返回四舍五入的poubds值
       }
       // Stonewt 转换为 double
       operator double() const
       {
           return pounds;
       }
}

 ......

int main()
{
  Stonewt poppins(9);
  double p_wt = poppins;  //隐式调用转换函数operator double() const
  cout << int (poppins) <<endl; //显示调用转换函数 operator int() const
   cout << (double)poppins <<endl;//显示调用转换函数  operator double() const
}

要注意转换函数会不会出现二义性混淆

原则上来说,最好使用显式转换,而避免隐式转换
在C++98中,关键字explicit不能用于转换函数,但C++11消除了这种限制,可以将运算符声明为显式的。

class Stonewt
{
private:
        double pounds;
public:
 
    explicit  operator int() const  
    explicit operator double() const
       
}
有了这些声明后,需要显式强制转换时才能调用说这些运算符

总结

  • 只有一个typeName参数的类构造函数将用于 将typeName型的数值 转换为 这个类的类型。
  • 被称为转换函数的特殊类成员运算符函数,用于将类对象转换为其他类型。
  • 关键字explicit可用于防止隐式转换,只允许显式强制转换

4. 实现加法Stonewt + double时的选择

要将double量和Stonewt量相加,有两种选择。
第一种方法自动转换法将加法运算符重载定义为友元函数,让Stonewt(double)构造函数将double类型的参数转换为Stonewt型的参数

friend operator+(const Stonewt &st1, const Stonewt & st2);

Stonewt(double);  //使用构造函数完成类型转换,为友元函数提供参数

这种方法优点在于可以同时适应Stonewt + double和double + Stonewt两种相加顺序,使程序更为简洁

第二种方法显式转换法将加法运算符重载为一个显式的使用double类型参数的函数

分别完成两种加法顺序
Stonewt operator+(double x);   //Stonewt + double
friend Stonewt operator+(double x, Stonewt & st);//double + Stonewt

这种方法程序略长,但是速度更快,节省调用构造函数自动转换的时间开销

你可能感兴趣的:(c++,primer,plus学习笔记,c++,c语言,开发语言)