于直接给对象赋值:

之前学过,如何给对象在初始化时进行赋值。

对于C++11来说,初始化方式有三种:

① man c = man{ "cc",1 };

② man d = { "dd",1 };

③ man f{ "ff",1 };

假如有一类M,他有两个私有成员a和b(int类型)。

于是新建一对象M q; 对象q使用默认构造函数(假如都赋值为0,这个不重要);

现在,我们想给对象q的第一个私有成员赋值,该怎么办?

这章刚学过运算符重载,难道要写个运算符重载么?隐式调用类对象,参数为赋值符号=后面的值?(我们知道,赋值符号是可以被成员函数重载的)

这显然太麻烦。

如果可以这样q=3,于是q的第一个成员被赋值3就十分方便了。

这是可行的,不是通过运算符重载,而是通过构造函数,构造函数设置一个参数时,使用q=3,则默认3为第一个参数,假设构造函数这么写。

M(int c = 1) { a = c; b = 1.1; };

那么使用q=3时,a将等于3,b将等于1.1(此刻相当于同时给2个私有成员赋值)。

如果我们只想给a赋值,不给b赋值,那么删掉b=1.1也是可以的(但这是默认构造函数,如果删除的话,创建新对象时,调用这个默认构造函数时则无法给b进行初始化)。

那么如果想利用赋值符号,同时给a和b赋值呢,且b使用我们给他的值,而不是上面的默认值1.1。办法是,继续使用构造函数,且这个构造函数有两个参数,赋值时使用列表化进行赋值(大括号)。

如:

M(int c, double d) { a = c;b = d; }

注意:这里不能给默认参数,否则在使用q=3这样时,会有二义性(即能匹配第一个默认构造函数,也能匹配这个构造函数)。

然后使用:q={5,4.4}; 这样的形式,可以同时给a和b赋值。

此时调用了有两个参数的构造函数。另外,需要注意的是,不能省略掉大括号,否则,会导致调用了默认构造函数。

如代码:

#include<iostream>class M
{int a;double b;
public:M(int c = 1) { a = c; b = 1.1; }  //带一个默认参数,默认构造函数M(int c, double d) { a = c;b = d; }    //两个参数,无默认参数,以避免二义性,构造函数void show() { std::cout << a << ", " << b << std::endl; };   //显示a和b的值
};int main()
{using namespace std;M q;   //创建对象q(调用默认构造函数)q.show();    //输出q = 5; //使用赋值符号,(此刻调用默认构造函数,但使用给的值作为参数)q.show();   q = { 5,4.4 }; //调用有两个参数的构造函数q.show();q = 9,3.3;  //虽然给了两个参数,但是只会调用默认构造函数  q.show();system("Pause");return 0;
}

显示:

1, 1.1
5, 1.1
5, 4.4
9, 1.1
请按任意键继续. . .

以上,将某个值直接赋给类对象,其原理是:使用类的构造函数,创造一个临时的对象,然后,将值赋给临时对象,再采用逐成员赋值的方式,将该临时对象的内容复制到目标对象之中。

这一过程,被称为 隐式转换 ,因为他是自动进行的,而不需要显式强制类型转换。

这种转换,称为是 类型转换

只有接受一个参数的构造函数,才能作为转换函数。有两个参数的构造函数,是不能用来转换类型的(就像上面说的,需要用大括号把两个参数都括起来)。但如果给第二个参数提供默认值,便又可以用于转换类型了(但前提是不会因此导致 二义性)。

将构造函数用于自动类型转换,在某些时候比较方便,但这种特性并非总是合乎需要的(比如说我们某个时候不需要他这么做,暂时举不出例子),因为这可能导致意外的类型转换(比如说只想给一个值赋值,但因为构造函数,给另外一个值赋了默认值)。

因此,C++新增了关键字 explicit,用于关闭这种隐式转换(但仍允许显式转换),即 显式强制类型转换

例如,double a=int (4.1);  便是一种 显式强制类型转换。double类型的4.1被强制转换为int类型(此时值为4),又被赋值给double类型,于是a=4。

构造函数改为:explicit M(int c = 1) { a = c; b = 1.1; }

此时,M q是可行的,

q=5是不可行的,

但是q = M(5);又是可行的。

另外,需要提一下的是,之前q = 9,3.3;只能导致9被赋值(调用了默认构造函数),但是修改后为 q = M(9,3.3); 则调用的是另一个构造函数了。

假如不使用explicit给构造函数(一个参数的,例如M(int c = 1)),那么,编译器将在什么启动隐式转换(使用构造函数,然后进行赋值)呢?

①声明一个类对象,并进行初始化。例如:M q(1);

②将值赋给对象时,例如:q=5;

③将值传递给接受类对象参数的函数时。例如:void abc(M t = 3); 这样。3相当于是函数的默认参数。

④返回值是类对象时,函数试图返回值时。就比如:

M abc()

{

return 3;

}

这个时候,相当于返回一个临时类对象,然后这个对象被初始化赋值为3,就像M q(3); 然后return q一样。只不过这里不是返回q,是返回一个临时的类对象

⑤在上述任意一种情况下,使用可转换为 可直接赋值给类对象的值的类型的 内置类型。

假如构造函数M(int c = 1) ,这个时候,需要用int类型的值给其赋值,例如q=3; 而我们知道,double类型可以转换为int类型,因此,我们也可以输入q=3.3; 这时,3.3被转换为int类型的3,然后int类型的3被隐式转换,赋值给类对象。这种情况也是允许的。

另外,以上隐式转换发生的前提是,函数不存在二义性,否则会提示错误。

转换函数:

隐式转换和显示转换,可以把一个比如说int类型的值,赋给类对象。

那么类对象是否能赋值给一个int类型的变量呢,也是可以的。

要进行这种转换,必须使用特殊的C++运算符函数—— 转换函数。

转换函数是 用户定义(不定义是不能用的)的强制类型转换,可以像使用强制类型那样使用他们。

转换函数的创建方法:

格式:operator 类型名();

关键点:

①转换函数必须是类方法(成员函数);

②转换函数不能指定返回类型(靠类型名);

③转换函数不能有参数(靠私有成员的值)。

例如,转换为int类型,其函数原型应该这么写:operator int();

函数定义如下写:

operator int()
{
return a;

}

这样,将返回私有成员a的值。

注意,假如有operator double(); 那么返回int值时调用第一个,返回double值时调用第二个。返回其他类型,会提示冲突。

如代码:

#include<iostream>class M
{int a;double b;
public:M(int c = 1) { a = c; b = 4.1; }  //带一个默认参数,默认构造函数void show() { std::cout << a << ", " << b << std::endl; }; //显示a和b的值operator int() { return a; }   //转换函数返回int值时调用这个operator double() { return b; }    //转换函数返回double值时调用这个
};int main()
{using namespace std;M q;   //创建对象q(调用默认构造函数)q.show();    //输出int a = q; //返回int值double b = q;  //返回double值double c = int(q);  //强制类型转换为int,因此返回int值cout << "a=" << a << ",b=" << b << ",c=" << c << endl;system("Pause");return 0;
}

显示:

1, 4.1
a=1,b=4.1,c=1
请按任意键继续. . .

自动应用类型转换:

假如,一个类只有一个转换函数,例如强制转换为int。

那么调用这个对象时,显示的便是其转换函数返回的值。

例如假如上面那个类方法,只有operator int()这个转换函数。那么当我们使用cout<<q<<endl; 时,

这个时候,程序便自动应用了类型转换,显示的是“1”。

但假如同时存在两个转换函数,再这样做的话,会提示二义性,因为程序并不知道你想要显示的是int类型的值还是double类型的值,除非你使用了强制类型转换。

注意:应谨慎的使用隐式转换函数。通常,最好选择仅在被显式的调用时才会被执行的函数。

例如:用一个返回值是你想要转换函数返回的值的函数,例如,使用operator()返回私有成员a,那么,用int M_to_int(){ return a;} 这样的函数,来返回私有成员a,调用时,则使用q.M_to_int();

总之:C++为类提供了下面的类型转换:

①利用构造函数,把基本类型转换为类 类型。

②利用转换函数,把类 类型转换为基本类型。

友元函数(运算符重载)和转换函数:

有类M,有类对象成员a,有int变量b。

有赋值,M c=a+b;

(1)假如有友元函数(或者运算符重载)的情况下:a+b将调用运算符重载函数,将函数返回值赋给类对象c。

(2)假如有转换函数,且类M的构造函数接受一个int参数。那么a将被转化为int值(根据转换函数),a+b的和(int值)作为参数,赋给类对象c。

假如有转换函数,但类M的构造函数不接受一个int参数(例如有两个参数且无默认参数),那么则提示出错,无法编译。

(3)假如既有运算符重载(“+”运算符),又有转换函数。

那么,根据实际测试来看,优先调用运算符重载函数或友元函数(假如他能完全匹配的话);

如果不能完全匹配,那么就可能导致二义性错误。

如代码:

#include<iostream>class M
{
int a;
double b;
public:
M(int c=3) { a = c; b = 4.4; }   //带一个默认参数,默认构造函数
void show() { std::cout << a << ", " << b << std::endl; };    //显示a和b的值
operator int() { return a; }    //转换函数返回int值时调用这个
friend int operator+(int c, M & q);
int operator+(int c) { return b + c; }
};int main()
{
using namespace std;
M q;    //创建对象q(调用默认构造函数)
M c = 3 + q;
M d = q + 3;
c.show();
d.show();
system("Pause");
return 0;
}
int operator+(int c, M & q)
{
return c + q.b;
}

显示:

7, 4.4
7, 4.4
请按任意键继续. . .

之所以结果为7,是因为优先使用了运算符重载函数(友元函数和成员函数),其用成员函数b和参数c相加,结果为7(也就是第一个数字)。

而若使用转换函数的话,其结果应为6,因为转换函数的返回值是3,3+3,其第一个数字应为6。——但具体为什么,推测是函数列表匹配度问题

①假如把3+q或者q+3,改为3.3+q和q+3.3,那么函数则会提示错误。原因在于,友元函数和成员函数不能完全匹配了,在匹配列表里优先级降低,成为了标准匹配(第三优先级)。而转换函数的返回值也是int类型,需要进行转换,因此也成为了标准转换(优先级也是第三级),因为匹配优先级相同,因此提示错误。

②又假如把q+3改为int(q)+3,那么首先对对象q执行强制类型转换,其为int值3(因为转换函数返回值为3),因此3+3=6,启用构造函数,于是第一个值便变成了6.

由于匹配优先级问题,如果想让q+3结果变为6,那么则在运算符重载函数修改为:int operator+(M&c) { return b + c.b; }  这时,转换函数的优先级则更高。

假如有:

#include<iostream>
class M
{int a;double b;
public:M(int c = 2) { a = c; b = 4.4; }  //带一个默认参数,默认构造函数void show() { std::cout << a << ", " << b << std::endl; }; //显示a和b的值operator int() { return a; }   //转换函数返回intfriend M operator+(M&a,M&c) { return a.b + c.b; }
};int main()
{using namespace std;M q;   //创建对象q(调用默认构造函数)M d = q + 3;d.show();system("Pause");return 0;
}

则d的输出结果为5,而不是8。使用的是转换函数,而不是运算符重载函数。原因我推测为:q+3,通过调用转换函数,被转换为2+3,然后将5赋值给了对象d。

而若使用运算符重载函数,则可能成为了用户定义的类型转换了。因此优先级不如转换函数。即使把q+3改为q+M(3)这种,将3强制转换为类M的临时对象,其优先级也没有转换函数高。

除非改为:M q;M w=3; M d=q+w; 这种形式,才会使用运算符重载函数。

总之,这种容易引起意料之外结果的,我觉得还是转换函数和运算符重载函数,二者取其一吧。或者放弃转换函数,而是改使用显式的转换函数(加关键字explicit)。

(一二四)给类对象赋值、以及类对象的返回值相关推荐

  1. 浅谈将子类对象赋值给父类对象

    最近对将子类对象赋值给父类对象有点心得,想和大家分享一下,但本人水平有限,请各位指正和批评.言归正传,下面是几个小例子,请大家看一看. 测试一 父类: public class Supclass {p ...

  2. Java中ListE对象赋值问题(引用传递和值传递)

    Java中List<E>对象赋值操作问题 业务需求是:取2个集合中的交集对象并返回.如下代码,busMap中key值和stocks中Map中的key值相等的对象则返回继续操作,也就是说剔除 ...

  3. js对象赋值、循环对象

    一般我们给对象的属性赋值时这样的: let a={}; a.value="值"; 如果在一个循环中,循环去给对象赋值的话,就会覆盖掉前面的值,而又想每次循环赋值的属性都不一样的话, ...

  4. IDEA设置注释模板——类模板和方法模板(参数,返回值)

    一.类的注释模板 文件------设置-------------编辑器-------文件和代码模板 模板: /*** ----------------------------------------- ...

  5. SpringMVC中通过@ResponseBody返回对象,Js中调用@ResponseBody返回值,统计剩余评论字数的js,@RequestParam默认值,@PathVariable的用法

    1.SpringMVC中通过@ResponseBody返回对象,作为JQuery中的ajax返回值 package com.kuman.cartoon.controller; import java. ...

  6. await把Promise解析为普通对象,async函数return的返回值是promise对象,await后转化为普通Object

    async函数return的是Promise,await Promise后得到Object 文章目录 前言 1.函数return的返回值Object,await后得到的是Object 2.async函 ...

  7. 如果在基类中将show声明为不带返回值的纯虚函数,正确的写法是()。

    答案:virtual void show()=0: 解析:virtual+返回值类型+函数名+()+=0

  8. 关于jQuery对象(类数组对象)以及DOM对象相互转化问题——[object Object]和[object HTMLInputElement]

    之前在某官网课程上看有关jQuery和bootstrap的相关教程,有一节课是教我们如何制作价格菜单的按钮以及总价问题 选中按钮,按钮样式会发生变化,右上角价格会自动运算 6个菜单的html结构差不多 ...

  9. C++对象赋值的四种方式

    1.  引用作为参数的方式传递. 1 GetObject(Object& obj) 2 { 3 obj.value = value1; 4 } 特点: 在外部构造一个对象. 把该对象以引用的方 ...

  10. 线程池,Volatile,原子性类AtomicInteger,乐观锁悲观锁,并发工具类Hashtable,ConcurrentHashMap类,Semaphore类

      目录 一.线程的状态 二.线程池 1.创建线程池的方式 1.1线程池-Executors默认线程池 1.2线程池-Executors创建指定上限的线程池 1.3线程池-ThreadPoolExec ...

最新文章

  1. VS Code搭建C/C++开发环境超详细教程
  2. 步进电机正反转实验_电工基础:帮你学会电机正反转双重互锁控制
  3. (boost::mp11::mp_rotate_left相关用法的测试程序
  4. 谷歌推出了其首款触屏笔记本电脑
  5. E. Don‘t Really Like How The Story Ends(代码未补)
  6. display函数怎么使用_使用网络构建复杂布局超实用的技巧,赶紧收藏吧
  7. 《你好李焕英》票房反超《唐探3》 成中国影史票房第五
  8. 阶段2 JavaWeb+黑马旅游网_15-Maven基础_第5节 使用骨架创建maven的java工程_18maven的java工程取mysql数据库...
  9. 从0开始学习C#第二天
  10. 如何在CSDN中免费下载资料
  11. 基于multisim14的函数信号发生器仿真
  12. 苹果电脑如何改id?这篇文章帮你搞定
  13. 北京高级项目经理市场需求
  14. layui form.js select的扩展插件(转自Author:@贤心)
  15. OCTA光学相干断层扫描血管成像
  16. 微信小程序(四) 节点查询 | wx.createSelectorQuery
  17. MATLAB约当标准型,用MATLAB求其约当标准型状态空间表达式
  18. 启发式算法(通俗解释)
  19. ARFoundation之路-环境配置(iOS)之二
  20. jquery国际化 i18n.js

热门文章

  1. 解决IE6下CSS兼容性的两把神器
  2. Update与JOIN使用
  3. Redis的安装过程步骤
  4. python经典数据类型
  5. 蓝牙广播错误码3_蓝牙简介—物理层(PHY)
  6. 倾斜模型精细化处理_【干货】7款倾斜摄影三维模型修补的软件介绍
  7. 自回归AR模型、移动平均MA模型、自回归移动平均ARMA模型
  8. 今天被公司安排给候选者进行初面,分享我的6道面试题
  9. 从事7年前端开发,有些经验想对转行学习前端的伙伴说说!
  10. 从事前端多年,我是这样看待三大框架的