C++的黑科技(深入探索C++对象模型)
周二面了腾讯,之前只投了TST内推,貌似就是TST面试了
其中有一个问题,“如何产生一个不能被继承的类”,这道题我反反复复只想到,将父类的构造函数私有,让子类不能调用,最后归结出一个单例模式,但面试官说,单例模式作为此题的解答不够灵活,后来面试官提示说,可以用友元+虚继承,可以完美实现这样一个类
当然那时我还不太明白,友元与虚继承我都极少接触过,只是知道有这些东西,回头搜了一下“不能被继承的类”的做法,具体如下:
1,声明一个类,CNoHeritance,构造函数为private,并声明友元类CParent;
2,让CParent虚继承CNoHeritance
这样CParent就成为一个可以被正常实例化,但又不能被继承的类
吴总当时评价说,“呵呵,虚继承,感觉完全是黑科技啊”
这个黑科技真是戳中我笑点,但想到C++经常有些奇妙的东西,现在想总结一下
1,C++构造函数的黑科技
对于阅读过进阶C++书籍的都该知道,编译器会在“需要”的时候,那么什么是需要的时候呢?四种情况:
- 1,“带有Default Constructor”的Member Class Object
- 2,“带有Default Constructor”的Base Class
- 3,“带有至少一个Virtual Function”的Class
- 4,“带有一个Virtual Base Class”的Class
自动合成的构造函数往往都是public,在派生类中,它的构造函数是可以被使用的,即派生类不会因此受到限制。
那么,如何能使派生类不能使用基类的函数或成员呢?
- private:只能由:1,该类中的函数;2,其友元函数访问
- protected:可以被:1,该类中的函数;2,其友元函数;3,派生类(子类)的函数访问
- public:可以被:1,该类中的函数;2,其友元函数;3,子类的函数;4,该类的对象访问
如果一个类的构造函数声明为private,则其派生类甚至该类的对象都不能访问,意味着两点:
- 1,该类不能被继承
- 2,该类不能由系统实例化,即它实例化的对象不会在栈内存上
那么怎么使用该类呢?一般而言,会通过该类的函数来创建
class A
{
private:A(){}
public:A& createA() { A* p=new A(); return *p; } };
然而,这样又引申一个问题:类没有实例化,如何能使用其成员函数呢?
答案是将该成员函数声明为static,这样不需要实例化即可访问,即将上述改为:
class A
{
private:A(){}
public:static A& createA() { A* p=new A(); return *p; } }; A Object=A::createA();
很明显,上面的实例化过程很不方便,简直是艰辛呀,单例模式的其中一种实现就是如此,在此先不讲。这样实现的类,不能被继承,但自己也不好过
so,如果用友元来实现,是怎么实现的呢?
声明一个类,及其友元
class A
{
private:A(){}friend class B; };
那么B是可以调用A的private的构造函数的,那么让B虚继承A会发生什么事呢?
由《深度探索C++对象模型》看到,B内存中将有一份A类的实体,调用A的构造函数构造的,这对于友元类B是可行的
class A
{
private:A(){}friend class B; }; class B : virtual A { };
那么这样的B能不能被继承呢?假设有个类继承了B,如下
class A
{
private:A(){}friend class B; }; class B : virtual A { }; class C : B { };
考虑到虚继承的特性,C也将调用A的构造函数构造出一个A,但!!C并不是A的友元类,所以根本不能执行A私有的构造函数,这段程序,如果不实例化C,编译器不会报错,但一旦实例化C,则将报错。
而B是可以正常实例化的一个类,这样就完美实现了一个不能被继承的类:B
2,C++构造函数初始化列表的黑科技
相比于构造函数的各种trick,C++的初始化列表就显得很容易了,只有那么一点要注意:
C++的初始化列表的赋值顺序,是与C++类里面成员变量的声明顺序相关,与初始化列表里的顺序无关
举个例子,以下就会出现莫名错误:
class A
{
public:A(int _x, int _y):y(_y), x(y){} public: int x; int y; };
根据声明顺序,在初始化列表中,是先完成x(y)
这个步骤,但此时y
并没有被赋值,所以得到的x是个随机的值。
3,C++虚函数的黑科技
C++虚函数的问题,几乎是面试必问,实际上需要了解的东西也挺多,我自己在前几次面试,都有些理解有误的地方,或者理解不够完善
这里总结几点吧(以下类都是针对有虚函数的类):
- 1,每个类都有虚函数表,这个虚函数表是在编译阶段构建,在代码段产生一个vtbl
- 2,每次实例化的时候,构造函数在前几个字节,产生一个指向虚函数表的指针,指向代码段的那个虚函数表
- 3,虚函数的实现与调整,是通过移动或变换虚函数表的指针来实现的。
- 4,纯虚函数是指只声明,但未被实现的虚函数,具有纯虚函数的类不能被实例化,为抽象类
4,C++拷贝构造函数的黑科技
C++的拷贝构造函数是C++默认的四个函数之一:构造函数、析构函数、赋值函数、拷贝构造函数
拷贝构造函数是一种特别的构造函数,在《深度探索C++对象模型》书中说,有三种情况,会导致拷贝构造函数被触发:
1,以一个object的内容作为另一个class object的初始值
class X {...} X x; X xx=x;
2,当object被当作参数传递给某个函数时
void foo(X x); X xx; foo(xx);
3,函数传回一个class object的时候
X foo_bar() {X xx;// ...return xx; }
一般情况下,如果没有提供explicit copy constructor时,会发生什么呢?
一个良好的编译器可以为大部分class objects产生bitwise copies,因为它们有bitwise semantics...
这里说的很神奇,好像我们不需要自己写copy constructor也没问题一样,实际上,bitwise copies在有些情况下是非常不推崇的
首先解释下什么是bitwise copies:这是指,在拷贝过来的时候,把class的内存直接位拷贝过来,即可以看成是内存拷贝(对应的有值拷贝)
位拷贝有很多问题,典型的一个,如果class里面含有分配内存的指针,那么它会将指针指向的地址直接拷贝过来:
class A
{
public:int *p;
};int main() { A a1; a1.p=new int[10]; A a2=a1; cout << a1.p << endl; cout << a2.p << endl; return 0; }
这里可以发现,a1.p
的地址与a2.p
的地址是一样的,那么,我分配的内存,该由哪个释放呢?我释放了,另一个怎么办呢?
实际上,这种拷贝方式在STL的string
里面肯定是要重写的,不能用位拷贝。
《深度探索C++对象模型》中,说class不展现出“bitwise copy semantics”有四种情况:
- 1,当class含有member object并且后者有一个copy constructor(声明或合成)
- 2,当class继承一个base class 而后者存在一个copy constructor的时候
- 3,当class声明了一个或多个virtual functions时
- 4,当class派生自一个继承串链,其中有一个或多个virtual base classes时
其实主要都是担心,指针在bitwise semantics下,随便复制可能会导致不可预料的错误
在这里说一下赋值函数与拷贝构造函数在触发上的区别:
当一个object从无到有时,触发的一定是拷贝构造函数,赋值函数只会在已有的object赋值时,才会触发
5,C++虚继承的黑科技
针对虚继承,可以坦承的一点就是
所有简单的东西,遇到虚继承,似乎都要单独拿出来讨论
转载于:https://www.cnblogs.com/qiaozhoulin/p/5227673.html
C++的黑科技(深入探索C++对象模型)相关推荐
- 探索停车黑科技,知位停车破局停车难题!
简介:打破"管理难"魔咒,知位停车来狙击! 车场无人值守 所有停车出入记录集中管理 还有各类停车相关场景创新能力 -- 一大波停车场黑科技来袭 手握众多停车场的你,准备好了吗? 一 ...
- 和顶会大佬一起,探索计算机视觉与未来黑科技
引言 你是否正对科研课题毫无头绪而困惑,对如何研读.写作和发表学术论文而苦恼.盐趣计算机视觉方向导师团队开发的计算机视觉与未来黑科技集训营将着力解决你科研道路上疑难问题. 这里不仅有名师授之以鱼更是授 ...
- 帮奶牛找对象?华为云AI黑科技大揭秘
AI可以用来做什么? 华为云BU总裁郑叶表示,"AI不是一个独立的产品,而是一种 '基本生产力',适用于大部分经济活动,将改变每一个行业.企业和职业,产生倍增效应." 于是,华为的 ...
- 阿里云首席科学家闵万里:我们为什么敢挑战一百年的制度,因为黑科技能为挽救生命抢来50%的可能性
如果急救车不被困在红灯下绝望等待,里面垂危的生命,或者还有希望.也许AI可以做到,让绿灯提前为他敞开生命之路. 编辑 | 鸽子 在这世界上,最令人绝望的等待就是在急救车上,无情的红色信号灯将它一路拦截 ...
- 北京冬奥又曝黑科技:连气象主播都是AI虚拟人
金磊 发自 凹非寺 量子位 | 公众号 QbitAI 拉满的黑科技,绝对是本届北京冬奥会的一大亮点. 无论是惊艳不已的开幕式,还是场馆内外的建设,无不因为科技,一次又一次地引发舆论的惊叹. 但万万没想 ...
- 【行业进展】谷歌4大AI黑科技部门,你可知
李毅 吉林大学 计算机视觉方向 作者 | 李毅(微信号:wxid_gdsyjoprueeaq12) 编辑 | 言有三 作为科技界的执牛耳者,谷歌在人工智能领域的实力有目共睹,本文将介绍谷歌AI黑科技产 ...
- 有哪些py写的黑科技_2020年汽车界又新增了哪些值得一提的黑科技产品?
一到12月份就意味着2020年的余额真的已经严重不足了,随之而来的各种年终总结.盘点.未来展望之类的自然也就成了不可避免的话题.那我们也不能免俗,今天就先汇总一下,在2020年的汽车界究竟新增了哪些值 ...
- 那些2019年会爆发的泛娱乐黑科技风口——网易MCtalk泛娱乐创新峰会揭秘
人群的多样化催生了不同的社交需求.如今,互联网上的社交行为已不局限于QQ和微信.近两年间,直播.短视频.图片.表情包等花样翻新的交互方式层出不穷,二次元社区等兴趣社交开始成为新集中地.音乐APP.拍照 ...
- 北京冬奥黑科技; 揭秘虎年春晚硬核科技;全球首款AR隐形眼镜问世;索尼3D显示技术路径曝光...
点击文章内容即可跳转至相应原文阅读 >> 祝大家开工大吉,虎年顺利! 北京冬奥从开幕式就黑科技曝了! 这一次的北京冬奥会,从开幕式就直接火了!首先是从冰立方中破冰而出并随着音乐冉冉升起的奥 ...
最新文章
- python语言有什么用-为什么现在很多人都使用Python语言有什么优势
- win7 旗舰版 网上邻居访问问题
- elasticsearch mapping之store
- Android学习之Shared Preference
- java 反射实现 工厂模式_java – 用反射实现工厂模式
- jquery数组怎么传给后台_我是如何让公司后台管理系统焕然一新的(下)封装组件...
- Linq在路上(序)
- 可以扦插的花有哪些?
- Confluence介绍与使用
- 数据库的varchar长度的限制以及对性能是否有影响
- 手机麦克风结构原理图_麦克风的分类和工作原理
- 亿图思维导图软件MindMaster Mac版常用快捷键汇总
- 外贸常用邮箱有哪些?163mail邮箱适合外贸用吗?
- mui 页面无法下滑拖拽 主要体现在华为手机浏览器
- 第三集 怪物学院 第十六章
- css如何将彩色图片变为黑白图片
- kafka的offset是个什么鬼。。
- 网络设备的MAC地址
- vue项目,把图片文件流转为base64格式以图片形式展示在前端
- 利用爬虫从一个百度贴吧页面下载图片
热门文章
- 关于ASP.NET页面打印技术的总结
- HttpHandler在IIS中的部署问题(扩展名映射)
- 深度linux卡在扫描硬盘,linux使用badblocks命令扫描硬盘排除故障
- mysql replace报错_Mysql中replace与replace into的用法讲解
- android activity启动流程_1307页!一线大厂Android面试全套真题解析!
- catia 工厂设计_SolidWorks、creo、UG哪个更适合机械设计?
- java百度地图地名定位地址_百度地图定位显示省市区街道名称,非常实用
- java按钮监听休眠_java-休眠监控解决方案
- python三维图的坐标_六维图见过么?Python 画出来了
- python直接执行代码漏洞_修复Python任意命令执行漏洞