IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 从C到C++___使用类(二)类的类型转换 -> 正文阅读

[C++知识库]从C到C++___使用类(二)类的类型转换

使用类(二)类的类型转换

上接使用类(一)

1. 将标准类型转换成类类型

有C语言基础的都知道,标准类型的是可以进行类型转换的,例如,数值类型中intdouble可以互相进行隐式类型转换。int类型和int *类型就无法进行隐式转换(或称自动类型转换),int *p=10;是错误的,但是我们可以使用显式类型转换(或称强制类型转换),int *p=(int *)10;是正确的。
实际上,C++允许我们给自定义类型(类)和标准类型进行互相转换。
看看下面这个例子:

#include<iostream>
#include<string>
class apple
{
    int number;
    std::string color;
    public:
    apple(int n=0,std::string c="no color")
    {
        number=n;
        color=c;
    }
    void display()const
    {
        std::cout<<"number: "<<number<<" color: "<<color<<std::endl;
    }
    void compare(const apple &a)const
    {
        if(a.number<number)
            std::cout<<"大"<<std::endl;
        else if(a.number==number)
            std::cout<<"相等"<<std::endl;
        else
            std::cout<<"小于"<<std::endl;
    }
   
};
int main()
{
    using namespace std;
    apple a;
    a.display();
    a={100,"red"};
    a.display();
    a=250;
    a.display();
    a=300.12312;
    a.display();
    a.compare(123.0);
}
number: 0 color: no color
number: 100 color: red
number: 250 color: no color
number: 300 color: no color
大

apple类使用了含有默认参数的构造函数,所以apple a;会创建对象a,并使用显式默认构造函数,进行默认初始化;
再之前,我们说过,构造函数只能用在对象初始化,但是实际上,它还适用于赋值语法
a={100,"red"};这句话是个赋值语句,它使用C++列表赋值语法,调用了构造函数。
a=250;这句话也是个赋值语句,乍一看,250是个int类型,而a是个apple对象,类型不匹配啊。
a=300.12312;这句话就更离谱了,它将double类型赋给了apple对象。
不幸的是,上面这两句都是正确的,因为当C++编译器,发现赋值语句两边类型不匹配时,它就会试着进行隐式类型转换,例如这里的250,300.12312类型均不匹配,那么编译器,就会去调用构造函数,而且这个构造函数一定要是可以接受一个参数且类型匹配或可以转换后匹配的。那么正好,apple类的构造函数,由于使用了默认参数,可以接受一个int类型的参数,那么自然的250可以传参,300.12312隐式转换成300传参,从而创造出新对象,再进行对象成员赋值,完成了赋值语句。
接受一个参数的构造函数为将类型与该参数相同(或转换后相同)的值,转换成类提供了蓝图
a.compare(123.0)更直接的点明了这一点,类方法compare()接受一个类对象参数,但是123.0double类型可以进行隐式转换而完成传参。那么,你完成了一个疯狂的举动–把标准类型参数传参给自定义类型,但是编译器不会发出warning
C++允许标准类型隐式转换成类,是危险的!
我们不希望这种隐式转换,这实在太危险了,最理想的状况是–如何让编译器只接受标准类型显式类型转换成自定义类?

  • explicit关键词

explicit明确的、清晰的意思,正如这个单词的意思,我们需要一个明确的构造函数,我们不希望构造函数称为隐式类型转换的帮凶,那么我们期待的explicit构造函数登场了。

#include<iostream>
#include<string>
class apple
{
    int number;
    std::string color;
    public:
    explicit apple(int n=0,std::string c="no color")
    {
        number=n;
        color=c;
    }
    void display()const
    {
        std::cout<<"number: "<<number<<" color: "<<color<<std::endl;
    }
    void compare(const apple &a)const
    {
        if(a.number<number)
            std::cout<<"大"<<std::endl;
        else if(a.number==number)
            std::cout<<"相等"<<std::endl;
        else
            std::cout<<"小于"<<std::endl;
    }
   
};
int main()
{
    using namespace std;
    apple a;
    a.display();
    a=(apple){100,"red"};
    a.display();
    a=(apple)250;
    a.display();
    a=(apple)300.12312;
    a.display();
    a.compare((apple)123.0);

}

看看上面这段代码,explicit apple(int n=0,std::string c="no color")是个明确的构造函数,它只允许显式类型转换,它告诉编译器,可以把标准类型转化成自定义类型,但是你必须进行显式转化,否则我会报错。
explicit关键词,会提供一个明确的构造函数,只允许标准类型显式类型转化成自定义类

apple b;//正确,显式默认初始化
b=12;//错误
b=(apple)12;//正确,显式类型转化
b=apple(12);//正确,等式右边是个构造函数的调用
apple c=13;//错误,不允许采用隐式类型转化,初始化apple对象
  • 类型转换与运算符重载。

#include<iostream>
#include<string>
class apple
{
    int number;
    std::string color;
    public:
    apple(int n=0,std::string c="no color")
    {
        number=n;
        color=c;
    }
    void display()const
    {
        std::cout<<"number: "<<number<<" color: "<<color<<std::endl;
    }
    void compare(const apple &a)const
    {
        if(a.number<number)
            std::cout<<"大"<<std::endl;
        else if(a.number==number)
            std::cout<<"相等"<<std::endl;
        else
            std::cout<<"小于"<<std::endl;
    }
    friend apple operator+(const apple &a,const apple &b)
    {
        apple ret;
        ret.number=a.number+b.number;
        return ret;
    }
   
};
int main()
{
    using namespace std;
    apple a{120,"red"};
    apple b;
    b.display();
    int x=250;
    b=x+a;
    b.display();

}
number: 0 color: no color  
number: 370 color: no color

上面这段代码,对+的重载,采用的友元函数的方式。b=x+a;首先x会调用构造函数自动转换成apple对象,再调用+的重载。但是如果我们采用的是类成员函数的方式重载+

apple apple::operator+(const apple & a)
{
    return (number+a.number);
}

就会出现问题,x+a,中xint类型,它无法调用类成员函数完成+重载。所以说,友元函数重载运算符相对来说,更适应自动类型转换,这是因为友元函数的参数比类成员函数多,所以更容易自动转换。

2.将类类型转化成标准类型

double n=a;这里a是一个apple对象。
这句话会报错,因为构造函数只用于从某种类型到类类型的转换,如果你想进行反方向的转换,必须使用转换函数

  • 转换函数

如果编译器发现,赋值语句右边是类类型,左边是标准类型,那么就会查看程序有没有定义了相应的转换函数。
转换函数的作用是,把类类型转换成标准类型。
转换函数的原型是:
operator typeName();

注意!,转换函数必须是类方法,不能指定返回类型,不能有参数
例如,你想把类类型转换成double类型,那么转换函数原型是:
operator double();

#include<iostream>
#include<string>
class apple
{
    int number;
    std::string color;
    public:
    apple(int n=0,std::string c="no color")
    {
        number=n;
        color=c;
    }
    void display()const
    {
        std::cout<<"number: "<<number<<" color: "<<color<<std::endl;
    }
    void compare(const apple &a)const
    {
        if(a.number<number)
            std::cout<<"大"<<std::endl;
        else if(a.number==number)
            std::cout<<"相等"<<std::endl;
        else
            std::cout<<"小于"<<std::endl;
    }
    operator int()
    {
        return number*100;
    }
    operator double()
    {
        return number/3.14;
    }
   
};
int main()
{
    using namespace std;
    apple a{120,"red"};
    int x=a;
    double y=a;
    cout<<x<<endl;
    cout<<y<<endl;

}
12000
38.2166

int x=a;double y=a;都是正确的,因为apple类中存在转换成intdouble的转换函数。
同样的,为了强调显式类型转换的重要性,转换函数也可以使用explicit修饰

3.小结

  • 接受一个参数的构造函数可以将其他类型的值转换成类类型。
  • 转换函数可以让类对象转换成其他类型的值。
  • 实际上不光是标准类型,我们甚至可以完成类与类之间的转换。总之,我们现在完成了类和任何类型的互相转换。
  • 实现运算符重载中,友元函数的方式可能更好。
  • explicit关键词,虽然规避了自动转换的危险性,但是可能要多敲很多代码。
  • 转换函数可能会引发重载解析失败,例如,我如果重载了+,并且我有转换函数operator int();,那么apple a=b+17;(b也是apple对象。)
  • 那么一种看法是:将17自动转换成apple对象,再调用类中+运算。
  • 另一种看法是:把b自动转换成int,然后调用int的加法。
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 10:25:04  更:2022-08-06 10:26:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 0:25:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码