C++中转换构造函数:将其它类型转换为当前类的类型

技术交流QQ群:1027579432,欢迎你的加入!

1.C++转换构造函数:将其它类型转换为当前类的类型

  • 在C/C++中,不同的数据类型之间可以相互转换。无需用户指明如何转换的称为自动类型转换(隐式类型转换),需要用户显式地指明如何转换的称为强制类型转换
        // 自动类型转换示例
        int a = 6;
        a = a + 3.12;  // 将小数转换为整数会抹掉小数点后面的数字
        // 强制类型转换示例
        int b = 100;
        int *p = &b;
        float *ptr = (float *)p;  // 将int *转换为float *只是简单地复制指针的值
    
  • 不管是自动类型转换还是强制类型转换,前提必须是编译器知道如何转换,例如,将小数转换为整数会抹掉小数点后面的数字,将int *转换为float *只是简单地复制指针的值,这些规则都是编译器内置的,我们并没有告诉编译器。总之,如果编译器不知道转换规则就不能转换,使用强制类型也无用,请看下面的例子:
        #include "iostream"
    
        using namespace std;
    
        // 如果编译器不知道转换规则就不能转换,使用强制类型也无用
        class complex{
            public:
                complex():m_real(0.0), m_img(0.0){}
                complex(double real, double img):m_real(real), m_img(img){}
            public:
                friend ostream & operator<< (ostream &out, const complex &c); // 友元函数
            private:
                double m_real;
                double m_img;
        };
    
        ostream & operator << (ostream &out, const complex &c){
            out << c.m_real << "+" << c.m_img << "i";
            return out;
        }
    
        int main(){
            complex a(10.0, 20.0);
            a = (complex) 25.5;  // 错误,转换失败
            return 0;
        }
    
  • 上面的例子中,25.5是实数,a是复数,将25.5赋值给a后,我们期望a的实部变为25.5,而虚部为0。但是,编译器并不知道这个转换规则,这超出了编译器的处理能力,所以转换失败,即使加上强制类型转换也无用。幸运的是,C++允许我们自定义类型转换规则,用户可以将其它类型转换为当前类类型,也可以将当前类类型转换为其它类型。这种自定义的类型转换规则只能以类的成员函数的形式出现。换句话说,这种转换规则只适用于类,本节先讲解如何将其它类型转换为当前类类型。

2.转换构造函数

  • 将其它类型转换为当前类类型需要借助转换构造函数(Conversion constructor)。转换构造函数也是一种构造函数,它遵循构造函数的一般规则,转换构造函数只有一个参数
        #include "iostream"
    
        using namespace std;
    
        /*---------------------------将其它类型转换为当前类的类型-------------------------------*/
    
        // 如果编译器不知道转换规则就不能转换,使用强制类型也无用
        class complex{
            public:
                complex():m_real(0.0), m_img(0.0){}
                complex(double real, double img):m_real(real), m_img(img){}
                complex(double real): m_real(real), m_img(0.0){}  // 转换构造函数,作用是将double类型的参数real转换成 complex类的对象,并将real作为复数的实部,将0作为复数的虚部
            public:
                friend ostream & operator<< (ostream &out, const complex &c); // 友元函数
            private:
                double m_real;
                double m_img;
        };
    
        ostream & operator << (ostream &out, const complex &c){
            out << c.m_real << " + " << c.m_img << "i";
            return out;
        }
    
        int main(){
            complex a(10.0, 20.0);
            cout << "a = " << a << endl;
            a = 25.5;  // 将赋值的过程转换成了函数调用的过程
            cout << "a = " << a << endl;
            return 0;
        }
    
  • 转换构造函数也是构造函数的一种,它除了可以用来将其它类型转换为当前类类型,还可以用来初始化对象,这是构造函数本来的意义。下面创建对象的方式是正确的:
        complex c1(23.1);  // 创建具名对象
        complex c2 = 24.3;  // 以拷贝的方式初始化对象,编译器先调用转换构造函数,将24.3转换为complex类型(创建一个complex类的匿名对象),然后再拷贝给c2。
        complex(3.21);  // 创建匿名对象
        c1 = complex(34.2);  // 创建一个匿名对象并将它赋值给 c1
    
  • 如果已经对+运算符进行了重载,使之能进行两个complex类对象的相加,那么下面的语句也是正确的:
        complex c1(15.6, 89.9);
        complex c2;
        c2 = c1 + 29.6;
        cout << c2 << endl;
    
  • 注意:为了获得目标类型,编译器会“不择手段”,会综合使用内置的转换规则和用户自定义的转换规则,并且会进行多级类型转换,例如:
    • 编译器会根据内置规则先将int转换为double,再根据用户自定义规则将double转换为complex(int --> double --> complex);
    • 编译器会根据内置规则先将char转换为int,再将int转换为double,最后根据用户自定义规则将double转换为complex(char --> int --> double --> complex)
          int main(){
              complex c1 = 100;  //int --> double --> complex
              cout << c1 << endl;
              c1 = 'A';  //char --> int --> double --> complex
              cout << c1 << endl;
              c1 = true;  //bool --> int --> double --> complex
              cout << c1 << endl;
              complex c2(25.8, 0.7);
              //假设已经重载了+运算符
              c1 = c2 + 'H' + true + 15;  //将char、bool、int都转换为complex类型再运算
              cout << c1 << endl;
              return 0;
          }
      

3.再谈构造函数

  • 构造函数的本意是在创建对象的时候初始化对象,编译器会根据传递的实参来匹配不同的(重载的)构造函数,几种形式的构造函数:
    • 默认构造函数: 就是编译器自动生成的构造函数,以complex类为例,它的原型为:
          complex();  // 没参数
      
    • 普通构造函数: 就是用户自定义的构造函数,以complex类为例,它的原型为:
          complex(double real, double img);  // 两个参数
      
    • 拷贝构造函数: 在以拷贝的方式初始化对象时调用,以complex类为例,它的原型为:
          complex(const complex &c);
      
    • 转换构造函数: 将其它类型转换为当前类类型时调用,以complex为例,它的原型为:
          complex(double real);  // 一个参数
      
  • 不管哪一种构造函数,都能够用来初始化对象,这是构造函数的本意。假设complex类定义了以上所有的构造函数,那么下面创建对象的方式都是正确的:
        complex c1();  // 调用complex()
        complex c2(10, 20);  // 调用complex(double real, double imag)
        complex c3(c2);  // 调用complex(const complex &c)
        complex c4(25.7);  // 调用complex(double real)
    
  • 除了在创建对象时初始化对象,其他情况下也会调用构造函数,例如:以拷贝的的方式初始化对象时会调用拷贝构造函数,将其它类型转换为当前类类型时会调用转换构造函数。这些在其他情况下调用的构造函数,就成了特殊的构造函数了。特殊的构造函数并不一定能体现出构造函数的本意。

4.对complex类的进一步精简

  • 上面的complex类中我们定义了三个构造函数,其中包括两个普通的构造函数和一个转换构造函数。其实,借助函数的默认参数,我们可以将这三个构造函数简化为一个,请看下面的代码:
        #include "iostream"
    
        using namespace std;
    
        /*---------------------------将其它类型转换为当前类的类型-------------------------------*/
    
        // 如果编译器不知道转换规则就不能转换,使用强制类型也无用
        class complex{
            public:
                complex(double real=0.0, double img=0.0):m_real(real), m_img(img){}
            public:
                friend ostream & operator<< (ostream &out, const complex &c); // 友元函数
            private:
                double m_real;
                double m_img;
        };
    
        ostream & operator << (ostream &out, const complex &c){
            out << c.m_real << " + " << c.m_img << "i";
            return out;
        }
    
        int main(){
            complex a(10.0, 20.0);  // 向构造函数传递2个实参,不使用默认参数
            cout << "a = " << a << endl;
            complex b(89.5);  // 向构造函数传递1个实参,使用1个默认参数
            cout << "b = " << b << endl;
            complex c;  // 不向构造函数传递实参,使用全部默认参数
            a = 25.5;  // 调用转换构造函数(向构造函数传递1个实参,使用1个默认参数)
            cout << "a = " << a << endl;
            return 0;
        }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容