从Qt谈到C++(一):关键字explicit与构造函数

2023-11-10

提出疑问

当我们新建了一个Qt的widgets应用工程时。会自动生成一个框架,包含了几个文件。其中有个mainwindow.h的头文件。就是你要操纵的UI主界面了。我们看看其中的一段代码:

class MainWindow : public QMainWindow
{
  Q_OBJECT//一个宏,暂不考虑

public:
  explicit MainWindow(QWidget *parent = 0);
  ~MainWindow();

private:
  Ui::MainWindow *ui;
};

这段代码定义了一个新的类MainWindow,继承自QMainWindow。我们可以看到在它的构造函数里,前面有一个关键字 explicit 。相信大家都对没有这个关键字的构造函数不陌生。那么这个 explicit 是起到什么作用的呢?

explicit研究

explicit是C++中的关键字,不是C语言中的。英文直译是“明确的”、“显式的”意思。出现这个关键字的原因,是在C++中有这样规定的基础上: 当定义了只有一个参数的构造函数时,同时也定义了一种隐式的类型转换。 先看类型转换。

类型转换

C/C++中,有很多类型转换。比如:

double a = 12.34;
int b = (int)a;
我们都知道这时b的值是12. 在变量前面加括号包裹的类型,就能实现显式的类型转换。这种叫做强制类型转换。顺便值得一提的是,C++中还支持这种强制类型转换的例子:
double a = 12.34;
int b = int(a);
除此之外,还有一种转换叫做 隐式类型转换。
double a = 12.34;
int b = a;
同样的,b的值也是12.虽然没有显式的转换类型,但是编译器会帮你自动转换。同样的,不仅是基本数据类型,自己定义的类和对象之间也存在这种转换关系。

隐式转换的场景

等于号与构造函数

比如你有一个类的对象A:

class A
{
public:
  A(int i)
  {
    a = i;
  }
  int getValue()
  {
    return a;
  };
private:
  int a;
};

你会发现,你在main函数中,使用下面的语句时是合法的:

A a = 10;
之所以类A的对象可以直接使用整型通过等于号来初始化,是因为这一语句调用了默认的单参数构造函数,其效果等价于  A temp(10); a(temp);

首先编译器执行A temp(10);在栈中创建了一个临时对象(假设叫做temp)。然后再调用对象a的拷贝初始化构造函数 a(temp) 给a初始化。然后临时对象temp销毁。这就是编译器做的隐式转换工作。你可以想到这样的隐式操作的结果和直接显示调用A a(10);的结果是一样的,但是隐式转换因为使用了拷贝构造函数所以在开销上会更高一些。当然这基本数据类型,或许不明显。如果一个复杂的对象,比如Qt的窗口。那么开销可想而知。

又如当你使用如下语句会不通过:

A a = "123";
因为没有参数为字符串的单参数构造函数。知道了这个,你修改一下就能通过了。
class A
{
public:
  A(int i)
  {
    a = i;
  }
  A(char * c)
  {
    a=c[0];
  }
  int getValue()
  {
    return a;
  };
private:
  int a;
};

函数调用

我们再定义一个函数print 用来打印A对象的值。

void print(A a)
{
  cout<<a.getValue();
};
在main函数中:
void main()
{
  print(10);
}
这样是可以编译运行的。虽然我们并没有创建一个类A的对象来传给print 函数。但是编译器默认会调用类A的单参数构造函数,创建出一个类A的对象出来。 

加上explicit

上面可以看出编译器会为你做一些,隐式的类型转换工作。这或许会让你感到方便,但是有时候却会带来麻烦。我们来做一个假设:

上面这个类A,只接受整型和字符串型。所以你想传递一个字符串 “2” 作为参数来构造一个新的对象。比如 A b = “2”; 然而,你却写错了,双引号写成了单引号变成了 A b = ‘2’; 然而这样并不会报错。编译器 会把 字符 ‘2’ 转型成 整型 也就是它的ascll码—— 50。为了避免这样我们不希望的隐式转换,我们可以加上explicit 关键字。

public:
  explicit A(int i)
  {
    a=i;
  }
这样就能避免隐式的类型转换了,当你误写成单引号的时候,就会报错。这样就只允许显示的调用单参数构造函数了。如 A a(10); A b("123"); 

不仅如此,在加上explicit之后,print函数也会报错了。因为编译器不会主动调用explicit标识的构造器。这就需要你自己显示的来调用了:

print(A(10));
当然了,是否应该禁止隐式转换是没有定论的,没有一种放之四海皆准的标准,具体看你的情景需要了。 
一般而言,显示调用构造器,能避免一些麻烦,让程序员手动来管理。很多人说C++难,因为很多东西对于程序员来说不是透明的,比如内存释放什么的,这个显式调用也是需要程序员自己动手的。然而我感觉这正是C++的魅力所在,C++给了程序员几乎等同于上帝的权力,所有一切都能自己掌控,还比如运算符重载的权力,甚至像Qt这样可以自定义slot和signal关键字(其实是宏),这在其他高级语言里是不可想象的。当然了,语言这东西,是仁者见仁智者见智的。没必要争论优劣。我一直认为的是:没有最优秀的语言,只有最合适的语言。编程语言本身没有优劣之分,但是不同程序员对于不同语言确有好恶之别。 

顺便一提

explicit关键字只用在类内部的声明中。在外部的实现部分不需要使用。

#include<iostream>
using namespace std;
class A
{
public:
  explicit A(int i);
  A(char * c)
  {
    a=c[0];
  }
  int getValue()
  {
    return a;
  };
private:
  int a;
};
A::A(int i)//无需再指明explicit
{
  a=i;
}
void print(A a)
{
  cout<<a.getValue();
};
void main()
{
  print(A(10));
}



from:  http://www.tuicool.com/articles/aqi67vE

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从Qt谈到C++(一):关键字explicit与构造函数 的相关文章

  • C++类内存分布

    书上类继承相关章节到这里就结束了 这里不妨说下C 内存分布结构 我们来看看编译器是怎么处理类成员内存分布的 特别是在继承 虚函数存在的情况下 工欲善其事 必先利其器 我们先用好Visual Studio工具 像下面这样一步一步来 先选择左侧
  • C++ STL : std::list

    练习下C STL中std list类的常用方法 方便以后查阅 如有不正确的地方 请读者及时指正 欢迎转载 谢谢 include
  • 构造函数属性为protected或者private时

    在c 中 不仅限于c 一个函数被声明为protected或者private时 那也就意味着不能被外部直接调用了 类的成员函数add 是private class cla private int add int a int b return
  • 字符检测:C语言ispunct()函数--判断字符是否为标点符号或特殊字符

    ispunct 函数用来检测一个字符是否为标点符号或特殊字符 其原型为 int ispunct int c 参数 c 为需要检测的字符 返回值 若 c 为标点符号或特殊符号 非空格 非数字和非英文字母 返回非 0 值 否则返回 0 注意 此
  • 虚函数在对象中的内存布局

    典型地 C 通过虚函数实现多态性 多态性的定义 无论发送消息的对象属于什么类 他们均发送具有相同形式的消息 对消息的处理方式可能随接受消息的对象而变 具体地说 在某个基类上建立起来的类的层次结构中 可以对任何一个派生类的对象中的同名成员函数
  • 显式调用构造函数和析构函数

    今天跟同事聊天 他说到STL源码有用到显示调用析构函数 试一了一下 果然能行 include lt iostream gt using namespace std class MyClass public MyClass cout lt l
  • 链接库介绍

    什么是库 计算机中 有些文件专门用于存储可以重复使用的代码块 例如功能实用的函数或者类 我们通常将它们称为库文件 简称 库 Library 以 C 语言为例 如下展示一个函数库 myMath c int add int a int b re
  • Modern C++的应用,实现golang中的defer

    modern C 实现 golang 的defer 关于RAII的一些思考 defer 的简介 注 没有 golang 语法基础的读者可以看看 反之 可以跳过 golang语法中的defer是什么 defer用来声明一个延迟函数 把这个函数
  • 单链表实现多项式相加

    这个小项目用C语言实现 代码中有我的注释 思路 用链表的每个节点存储表达式的每一项 因此每个链表就是一个表达式 链表节点类型的定义 struct Node DataType elem 项的系数 Variate ch 常量和变量的标志 规定如
  • C++ 创建共享内存

    共享内存用于实现进程间大量的数据传输 共享内存是在内存中单独开辟一段内存空间 这段内存空间有自己特有的数据结构 包括访问权限 大小和最近访问时间等 1 shmget函数 include
  • C++ 构造函数和析构函数是否可以继承?

    先看一个例子 cpp view plain copy include
  • 字符编码与C++

    背景 C 的项目 字符编码是一个大坑 不同平台之间的编码往往不一样 如果不同编码格式用一套字符读取格式读取就会出现乱码 所以本文旨在对字符编码的知识做一个大概的梳理 字符编码定义 计算机是以二进制的形式来存储数据的 它只认识 0 和 1 两
  • 将2个链表交替合并成一个链表

    将带有头结点的2个线性单链表交替有规则的合并成为一个链表 今天做这个的时候 又犯了以前一个愚蠢的错误 对于有些代码 为了方便我就直接复制了 编译器查出来有错 我一直看不出来错误在哪里 那一块我直接就忽略了 代码不敢随便复制 我画个图我认为直
  • C语言中的快速排序库函数

    前言 由于自己的记性一直不好 总是记不住c语言中的快速排序函数 于是自己写下博客来记录一下 快速排序库函数 c语言中的快速排序库函数如下 注意 库函数是在stdlib h头文件中 qsort arr length size t cmp 其中
  • c语言中的字符数组和字符串之间的关系

    一 字符串的结束标志 1 很多时候我们都是可以看到相关的内容就是 使用数组来存储字符串 也就是我们经常会使用到sizeof 和这个函数 而 这个函数只是求出当前该数组的最大容量 而不是数组中实际存放的内容 我们一般都是需要使用 0 来表示字
  • 有些运行符不能重载为友元函数,它们是:=,(),[]和->。

    原因 有人说是因为 C 规定赋值运算符 只能重载为类的非静态成员函数 而不可以重载为类的友元函数 不能重载为类的静态成员应该比较容易理解 因为静态成员函数是属于整个类的 不是属于某个对象的 它只能去操作类静态数据成员 而赋值运算符 是基于对
  • struct和typedef struct彻底明白了

    struct和typedef struct 分三块来讲述 1 首先 注意在C和C 里不同 在C中定义一个结构体类型要用typedef typedef struct Student int a Stu 于是在声明变量的时候就可 Stu stu
  • C++ 虚函数表解析

    C 虚函数表解析 陈皓 http blog csdn net haoel 前言 C 中的虚函数的作用主要是实现了多态的机制 关于多态 简而言之就是用父类型别的指针指向其子类的实例 然后通过父类的指针调用实际子类的成员函数 这种技术可以让父类
  • 如何零基础自学c/c++语言?

    现在零基础学习C C 无非就两种方法 一种是自学还有 一种就是报班学习 关于报班学习在这里就不多说了 那么今天就说怎么从零基础开始自学C C 编程吧 先学习C语言入门 那么问题来了 怎么去学习C语言呢 一开始肯定是要看书 这里推荐的入门书籍
  • 类的数组成员变量的初始化

    使用STL标准模板库之后 编程时已经很少使用数组和指针 相反 多使用序列容器vector代替之 但事实并不这么理想 在迫不得已的情况下 我们还是会选择使用数组 这里介绍一下当数组作为类的成员变量时 应该怎么对它 数组 进行初始化 在类的构造

随机推荐

  • idea 查找未使用的代码

    1 查找未使用的代码 输入 unused declaration 2 处理方法 未使用的类 方法的处理方式 Safe delete 删除 Comment out 注释掉 Add as Entry Point 添加为入口点 未用过 Suppr
  • Python 第一章 基础知识(2) 交互式解释器

    第一章 基础知识 2 交互式解释器 Python 2 7 3 default Apr 30 2012 21 18 11 GCC 4 7 0 20120416 Red Hat 4 7 0 2 on linux2 Type help copyr
  • [需求]需求分析能力之一:用流程图说话

    从江西项目上回来 就有一堆的事情 总算可以告一段落了 那是阮阮还在泰安基地工作的时候 有一次 我跟她聊天 说起需求能力 我说 我并不是比别人聪明 而是掌握了需求的技巧 可以很快的提取 整理到系统需求 因此今天 就是 我把这种需求能力免费告诉
  • Open3d学习计划——4(网格)

    Open3d学习计划 4 网格 欢迎大家关注 点云PCL 公众号 进入群聊一起学习 open3d有一种被称为TriangleMesh的3d三角网格的数据结构 下面的代码展示了如何从一个ply文件读取三角网格数据并且打印它的顶点和三角形 pr
  • Android知识点博客集1

    http blog csdn net true100 article category 2849593
  • 对于正则化项L1,L2范数的理解

    对于正则化项L1 L2范数的理解 L2范数正则化 L1范数正则化 L1范数正则化与L2范数正则化的作用区别 L1范数正则化为什么比L2范数正则化更容易获得稀疏解 之前讲到了利用正则化项降低过拟合 这里再总结一下L1 L2这两种范数作为正则化
  • python练习题(四):有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数

    count 0 for i in range 1 5 for j in range 1 5 for k in range 1 5 if i j and i k and j k print i j k count 1 print 总计 d c
  • Python爬虫逆向实战案例(五)——YRX竞赛题第五题

    题目 抓取全部5页直播间热度 计算前5名直播间热度的加和 地址 https match yuanrenxue cn match 5 cookie中m值分析 首先打开开发者工具进行抓包分析 从抓到的包来看 参数传递了查询参数m与f 同时页面中
  • 图常见算法-广度优先算法python实现

    广度优先算法 广度优先搜索 breadth first search BFS 该算法用于解决两类问题 1 节点A到节点B是否有路径 2 节点A到节点B的哪条路径最短 算法实现思想 图的建立使用了散列表 双端队列使用了deque 为了避免死循
  • 吴恩达深度学习编程作业 pytorch 版rnn时间序列

    import numpy as np import pandas as pd import matplotlib pyplot as plt import torch from torch import nn from torch auto
  • Pycharm无法导入第三方库终极解决方法!!!

    折磨了我两天 远程问人也无果 百度上的答案更是五花八门 却无一起作用 最终 在一次次尝试下得出解决方案 报错 重点看它的Proposed solution Make sure you use the correct version of p
  • Python分析《我们与恶的距离》豆瓣剧评

    Python分析 我们与恶的距离 豆瓣剧评 我们与恶的距离 是一部非常有深度的台剧 演员的演技无可挑剔 剧情编排也很新颖 而且逻辑严谨 剧情环环相扣 不拖泥带水 剧中出了很多火遍全网的台词 如 所以爱会消失 对不对 我们都是好人 不知道为什
  • 黑苹果升级到catalina 10.15.6问题

    配置 dell 9570 从catalina 10 15 4升级到 10 15 5 无任何问题 今天手贱 升级到10 15 6 问题来了 主要表现是 有时启动不成功 另外就是三指拖动窗口失效 发现xcode也升级了 可能需要重新编译 也许有
  • node.js操作mysql数据库和sql server数据库

    node js操作mysql数据库和sql server数据库 这一篇博客主要是数据库的 特别是sql server数据库 首先讲解mysql 因为mysql的操作并不复杂 所以讲解会很快 第一步 第一步肯定是导入模块 和配置配置项 con
  • 关于单舵轮AGV运动学模型整理笔记

    2 1 单舵机图画 2
  • Daily report和weekly report很重要

    7 Daily report和weekly report很重要很多程序员宁愿多写程序 也不愿意写report 觉得十分麻烦 而又无聊 但是Daily report weekly report真的非常的重要 首先report可以帮助你管理自己
  • 多个el-tab共用一个表单校验问题

    需求是这样的 有多项tab 切换不同的tab 显示的输入框是一样的 但有的tab需要必填 有的tab则不需要必填 效果图如下 带星号和不带星号
  • 西门子PN协议分布式IO模块

    BL200Pro版本支持OPC UA MQTT和Modbus TCP协议 分布式I O模块系统由于具有可靠度高 容易扩展 设置容易 网络布线方便等特性 适用于分散地区的应用 广泛应用于数据收集和各种控制 产品广泛应用于物联网 智慧工厂 智慧
  • 【进程间通信(三)】共享内存(system v)

    一 共享内存通信的实现原理 其实进程之间通信的本质就是让通信双方看见同一份内存 这样当这份内存的数据发生改变的时候 双方进程都能直接观察到结果 这份内存存放在共享数据区 由操作系统提供并维护 共享内存的特性是只会储存进程之间通信产生的临时变
  • 从Qt谈到C++(一):关键字explicit与构造函数

    原文 http blog csdn net guodongxiaren article details 24455653 主题 Qt 提出疑问 当我们新建了一个Qt的widgets应用工程时 会自动生成一个框架 包含了几个文件 其中有个ma