第11章 使用类——类型转换(一)将内置类型转换为自定义类型

本文章是作者根据史蒂芬·普拉达所著的《C++ Primer Plus》而整理出的读书笔记,如果您在浏览过程中发现了什么错误,烦请告知。另外,此书由浅入深,非常适合有C语言基础的人学习,感兴趣的朋友可以自行阅读此书籍。

类型转换

自动转换也叫隐式转换。 强制类型转换也叫显式强制类型转换。

将一个标准类型变量的值赋给另一种标准类型的变量时,如果这两种类型兼容,则C++自动将这个值转换为接收变量的类型。
如下面的语句:

long count = 8; //int类型转换成了long类型
double time = 11; //int类型转换成了double类型
int side = 3.33; //将double类型数据转换成了int数据,从浮点转换为整型,小数消失

上述的操作可以成功,是因为在C++中,各种数值类型都表示相同的东西——一个数字,同时C++包含用户进行转换的内部规则。

C++语言不自动转换不兼容的类型。例如下面的语句是非法的:

int *p = 10;

虽然指针的地址可能是个数字,但从概念上说,整数和指针完全不同。然而,在无法自动转换时,可以使用强制类型转换。

int *p = (int *)10;  //这个语句可以编译成功,即使它可能没有意义。

可以将类定义成与基本类型或另一个类相关,使得从一种类型转换为另一种类型是有意义的。因此,我们需要在设计类的同时,使类具有自动转换,或强制类型转换的功能。

假设我们设计一个类,该类可以进行重量换算,比如将英石转换为磅(1英石 = 14磅)。
类声明如下:

//stonewt.hpp
#ifndef _STONEWT_HPP_
#define _STONEWT_HPP_

class Stonewt
{
private:
  enum {Lbs_per_stn = 14};
  int stone;
  double pds_left;
  double pounds;

public:
  Stonewt(double lbs);
  Stonewt(int stn, double lbs);
  Stonewt();
  ~Stonewt();
  void show_lbs()const;
  void show_stn()const;
};
#endif

类方法实现如下:


//stonewt.cpp

#include <iostream>
using std::cout;
#include "stonewt.hpp"

Stonewt::Stonewt(double lbs)
{
  stone = int (lbs) / Lbs_per_stn;
  pds_left = int (lbs) % Lbs_per_stn + lbs - int (lbs);
  pounds = lbs;
}
Stonewt::Stonewt(int stn, double lbs)
{
  stone = stn;
  pds_left = lbs;
  pounds = stn * Lbs_per_stn + lbs;
}
Stonewt::Stonewt()
{
  stone = pds_left = pounds = 0;
}
Stonewt::~Stonewt()
{

}
void Stonewt::show_stn()const
{
  cout << stone << " stone," << pds_left << " pounds\n";
}
void Stonewt::show_lbs()const
{
  cout << pounds << " pounds\n";
}

将内置类型转换为自定义类型

在C++中,接受一个参数的构造函数 为 将类型与参数相同的值转换为类提供了蓝图,因为下面的构造函数用于将double类型的值转换为Stonewt类型。

Stonewt(double lbs);

即,可以编写这样的代码:

Stonewt myCat;
myCat = 19.6;

程序将使用构造函数Stonewt(double)来创建一个临时的Stonewt对象,并将19.6作为初始化值。随后,采用逐成员赋值方式将该临时对象的内容复制到myCat中。这一过程称为隐式转换,因为它是自动进行的,而不需要显式强制类型转换。

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

Stonewt(int stn, double lbs);

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

Stonewt(int stn, double lbs = 0);

在部分场景中,我们可能并不需要隐式转换,C++新增了关键字explicit,用于关闭这种自动特性,也就是说,需要这样声明构造函数:

explicit Stonewt(double lbs);

这样就不能进行隐式转换了,只能进行显式转换,即显式强制类型转换:

Stonewt myCat;
myCat = 19.6;           //失败,不需要隐式转换
myCat = Stonewt(19.6);  //成功,显式转换
myCat = (Stonewt)19.6;  //成功,老风格的显式转换。

那么问题来了,编译器在什么时候将使用Stonewt(double)函数呢?

隐式转换的生效场景

如果在声明中使用了关键字explicit, 则Stonewt(double)将只用于显式强制类型转换,否则还可以用于下面的转换:
  • 将Stonewt对象初始化为double值时。
  • 将double值赋给Stonewt对象时。
  • 将double值传递给接受Stonewt参数的函数时。
  • 返回值被声明为Stonewt的函数试图返回double值时。
  • 在上述任意一种情况下,使用可转换为double类型的内置类型时。

需要注意的是,在部分场景下,当且仅当转换不存在二义性时,才允许进行隐式转换。

假设存在如下的构造函数,

Stonewt(double);
StoneWt(long);

那么下列语句编译就会出错:

Stonewt Jumbo(7000),

因为7000是int类型,既可以转换成double类型,也可以转换成long类型,存在二义性。

我们简单使用下上面的类:

//stone.cpp
#include <iostream>
#include "stonewt.hpp"

using std::cout;

void display(const Stonewt& st, int n);
int main()
{
    Stonewt incognito = 275;
    Stonewt wolfe(285.7);
    Stonewt taft(21, 8);

    cout << "incognito weighed ";
    incognito.show_stn();
    cout << "wolfe weighed ";
    wolfe.show_stn();
    cout << "taft weighed ";
    taft.show_lbs();

    incognito = 276.8;
    taft = 325;

    cout << "After dinner, incognito weighed ";
    incognito.show_stn();
    cout << "After dinner, taft weighed ";
    taft.show_lbs(); 

    //传递对象
    display(taft, 2);        

    //隐式转换类型
    display(234, 2);
    return 0;
}

void display(const Stonewt& st, int n)
{
    for (int i = 0; i < n; i++)
    {
        st.show_stn();
    }    
}

程序运行结果如下:

incognito weighed 19 stone,9 pounds
wolfe weighed 20 stone,5.7 pounds
taft weighed 302 pounds
After dinner, incognito weighed 19 stone,10.8 pounds
After dinner, taft weighed 325 pounds
23 stone,3 pounds
23 stone,3 pounds
16 stone,10 pounds
16 stone,10 pounds

程序说明:
当构造函数只接受一个参数时,可以使用下面的格式来初始化类对象:

Stonewt incognito = 275;

这等价于下面两种格式

Stonewt incognito(275);
Stonewt incognito = Stonewt (275);

但是后两种格式可用于接受多个参数的构造函数。

接下来,两条赋值语句,

incognito = 276.8;
taft = 325;

第一个赋值语句使用接受double参数的构造函数,将276.8转换为一个Stonewt值,这将把incognito的pound成员设置为276.8,同时设置stone和pds_left成员。

第二条赋值语句将一个int值转换为double类型,然后使用Stonewt(double)来设置全部3个成员。

最后,请注意下面的函数调用:
display(422, 2);

display的原型表明,第一个参数应是Stonewt对象。遇到int参数时,编译器查找构造函数Stonewt(int),以便将该int转换为Stonewt类型。由于没有找到这样的构造函数,因此编译器寻找接受其他内置类型的构造函数。Stonewt(double)满足这样的要求,因此编译器将int转换为double,然后使用Stonewt(double)将其转换为一个Stonewt对象。

posted @   superbmc  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示