C++的黑科技(深入探索C++对象模型)

来源:http://www.cnblogs.com/qiaozhoulin/p/5227673.html


“如何产生一个不能被继承的类”,这道题我反反复复只想到,将父类的构造函数私有,让子类不能调用,最后归结出一个单例模式,但面试官说,单例模式作为此题的解答不够灵活,后来面试官提示说,可以用友元+虚继承,可以完美实现这样一个类

当然那时我还不太明白,友元与虚继承我都极少接触过,只是知道有这些东西,回头搜了一下“不能被继承的类”的做法,具体如下:

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++虚继承的黑科技

针对虚继承,可以坦承的一点就是

所有简单的东西,遇到虚继承,似乎都要单独拿出来讨论



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/496754.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

物联网测试完整解决方案 | 为你的芯片、模块、终端保驾护航

来源&#xff1a;物联网智库2017年被视为物联网商用元年。窄带物联网标准正式冻结&#xff0c;国内运营商重点布局以及设备制造商的强力推动……自此&#xff0c;物联网规模化商用迈入了快车道。然而&#xff0c;与传统的智能手机类似&#xff0c;基于NB-IoT技术的物联网设备在…

mbp网速很慢_苹果笔记本上网很慢怎么回事?macbook无线上网慢的解决方法

Mac笔记本连接到WiFi之后,发现网速很慢&#xff0c;简直让人无法接受&#xff0c;打开一个页面都要等上好几分钟。苹果笔记本上网很慢的原因有很多&#xff0c;检查一下WiFi信号是满格&#xff0c;那么排除所在网络带宽限制的原因。想要解决此问题也简单&#xff0c;只要修改手…

Java并发编程实战~CyclicBarrier

用 CyclicBarrier 实现线程同步 线程 T1 和 T2 要做到步调一致&#xff0c;都完成后通知到线程 T3 创建了一个计数器初始值为 2 的 CyclicBarrier&#xff0c;你需要注意的是创建 CyclicBarrier 的时候&#xff0c;我们还传入了一个回调函数&#xff0c;当计数器减到 0 的时候…

梦中的婚礼

让女孩听了陶醉的曲子梦中的婚礼 :http://player.youku.com/player.php/sid/XMjA1ODc3MTI/v.swf 电脑键盘弹奏 梦中的婚礼 :http://player.youku.com/player.php/sid/XMzMzNzQyMDA/v.swf 转载于:https://www.cnblogs.com/cube/archive/2008/07/19/1246736.html

C++学习之路 | PTA乙级—— 1084 外观数列 (20 分)(精简)

1084 外观数列 (20 分) 外观数列是指具有以下特点的整数序列&#xff1a; d, d1, d111, d113, d11231, d112213111, … 它从不等于 1 的数字 d 开始&#xff0c;序列的第 n1 项是对第 n 项的描述。比如第 2 项表示第 1 项有 1 个 d&#xff0c;所以就是 d1&#xff1b;第 2 项是…

DeepMind提出图形的「深度生成式模型」,可实现「任意」图形的生成

原文来源&#xff1a;arXiv作者&#xff1a;Yujia Li、Oriol Vinyals、Chris Dyer、Razvan Pascanu、Peter Battaglia 「雷克世界」编译&#xff1a;嗯~阿童木呀、KABUDA一般来说&#xff0c;图形是基本的数据结构&#xff0c;它在诸如知识图、物理和社会交互、语言和化学等许多…

eviews曲线图怎么做_【干货速递】Eviews:你不可不知的经典问答!

更多精彩内容请关注211统计课堂计量经济学是分析啥的&#xff1f;01计量经济学的主要用途或目的主要有两个方面&#xff1a;1.理论检验。这是计量经济学用途最为主要的和可靠的方面。这也是计量经济学本身的一个主要内容。2.预测应用。从理论研究和方法的最终目的看&#xff0c…

感恩的心

不气歌&#xff08;一&#xff09; 他人气我我不气&#xff0c;我本无心他来气。 倘若生气中他计&#xff0c;气出病来无人替。 请来医生把病治&#xff0c;反说气病治非易。 茶不思来饭无味&#xff0c;通宵达旦不入睡。 倘你伸腿离我去&#xff0c;撇我一人活受罪。 奉…

Python-OpenCV 图像与视频处理

来源&#xff1a;https://segmentfault.com/a/1190000003742481 一直断断续续的用过几次 OpenCV&#xff0c;感觉熟练掌握它的使用方法已经变的非常必要了&#xff0c;正好找到一个很不错的英文教程&#xff0c;就以此为起点&#xff0c;详细记录一下对 OpenCV 的学习过程吧。 …

C++学习之路 | PTA乙级—— 1086 就不告诉你 (15 分)(精简)

1086 就不告诉你 (15 分) 做作业的时候&#xff0c;邻座的小盆友问你&#xff1a;“五乘以七等于多少&#xff1f;”你应该不失礼貌地围笑着告诉他&#xff1a;“五十三。”本题就要求你&#xff0c;对任何一对给定的正整数&#xff0c;倒着输出它们的乘积。 输入格式&#xf…

Java并发编程实战~并发容器

在容器领域一个容易被忽视的“坑”是用迭代器遍历容器&#xff0c;例如在下面的代码中&#xff0c;通过迭代器遍历容器 list&#xff0c;对每个元素调用 foo() 方法&#xff0c;这就存在并发问题&#xff0c;这些组合的操作不具备原子性。 List list Collections.synchronize…

12c oracle 激活_Windows运维之Windows server 2016 安装及ORACLE 12C 安装

本文主要向大家介绍了Windows运维之Windows server 2016 安装及ORACLE 12C 安装&#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家学习Windows运维有所帮助。首先创建虚拟机&#xff0c;选择windows server 2016启动虚拟机&#xff0c;进入安装界面&#xff0c;语言…

工信部:筹建全国首个区块链和分布式记账标准化技术委员会

作者&#xff1a;李秀琴在3.15即将来临之时&#xff0c;我国工信部给区块链行业又带来了一大利好消息。3月12日&#xff0c;工业和信息化部&#xff08;以下简称工信部&#xff09;在官网发布公告称&#xff0c;其正在就筹建全国区块链和分布式记账技术标准化技术委员会事宜开展…

PowerDesigner11技巧

文章来源&#xff1a;http://blog.csdn.net/edeed/archive/2006/02/10/596271.aspx 1、安装PD v11.0版 2、由pdm生成建表脚本时&#xff0c;字段超过15字符就发生错误&#xff08;oracle&#xff09; 原因未知&#xff0c;解决办法是打开PDM后&#xff0c;会出现Database的菜单…

C++学习之路 | PTA乙级—— 1087 有多少不同的值 (20 分)(精简)

1087 有多少不同的值 (20 分) 当自然数 n 依次取 1、2、3、……、N 时&#xff0c;算式 ⌊n/2⌋⌊n/3⌋⌊n/5⌋ 有多少个不同的值&#xff1f;&#xff08;注&#xff1a;⌊x⌋ 为取整函数&#xff0c;表示不超过 x 的最大自然数&#xff0c;即 x 的整数部分。&#xff09; 输入…

Python 的 requests 库的用法

Python爬虫利器一之Requests库的用法&#xff1a;http://cuiqingcai.com/2556.html Python利用Requests库写爬虫&#xff08;一&#xff09;&#xff1a;http://www.jianshu.com/p/e1f8b690b951 Python-第三方库requests详解&#xff1a;http://blog.csdn.net/shanzhizi/articl…

Java并发编程实战~Lock

再造管程的理由 synchronized导致死锁问题&#xff0c;提出了一个破坏不可抢占条件方案&#xff0c;但是这个方案 synchronized 没有办法解决。原因是 synchronized 申请资源的时候&#xff0c;如果申请不到&#xff0c;线程直接进入阻塞状态了&#xff0c;而线程进入阻塞状态…

wpf 点击按钮弹出选择框_WPF-PopupWindow wpf右下角弹出框,通过按钮调用,类似QQ CSharp C#编程 238万源代码下载- www.pudn.com...

文件名称: WPF-PopupWindow下载收藏√ [5 4 3 2 1 ]开发工具: C#文件大小: 90 KB上传时间: 2013-07-24下载次数: 19详细说明&#xff1a;wpf右下角弹出框&#xff0c;通过按钮调用&#xff0c;类似QQ弹出框-wpf lower right corner of the pop-up box文件列表(点击判断是否…

2018 年人工智能会怎么发展?这里有 8 个预测

来源&#xff1a;36氪普华永道发布了一份报告&#xff0c;对人工智能在2018年的发展趋势进行了研究&#xff0c;并做出了8项预测。人工智能非常复杂&#xff0c;而且发展速度很快。任何人都不可能对其未来几年的发展方向做出准确的预测。但就人工智能在2018年的发展趋势来说&am…

异常单据锁定涉及的数据库表

在软件使用过程中出现单据锁定提示&#xff0c;进入系统管理清除单据锁定&#xff0c;再进入软件还是提示单据锁定.&#xff08;包括&#xff1a;审核凭证时提示单据锁定&#xff1b;不能录入期初余额&#xff0c;提示单据锁定&#xff1b;银行对帐单锁定 等情况&#xff09; 造…