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++虚继承的黑科技
针对虚继承,可以坦承的一点就是
所有简单的东西,遇到虚继承,似乎都要单独拿出来讨论
待续
C++的黑科技的更多相关文章
- ACM: FZU 2105 Digits Count - 位运算的线段树【黑科技福利】
FZU 2105 Digits Count Time Limit:10000MS Memory Limit:262144KB 64bit IO Format:%I64d & ...
- 黑科技项目:英雄无敌III Mod <<Fallen Angel>>介绍
英雄无敌三简介(Heroes of Might and Magic III) 英3是1999年由New World Computing在Windows平台上开发的回合制策略魔幻游戏,其出版商是3DO. ...
- [自己动手玩黑科技] 1、小黑科技——如何将普通的家电改造成可以与手机App联动的“智能硬件”
NOW, 步 将此黑科技传授予你~ 一.普通家电控制电路板分析 普通家电,其人机接口一般由按键和指示灯组成(高端的会稍微复杂,这里不考虑) 这样交互过程,其实就是:由当前指示灯信息,按照操作流程按相应 ...
- 迪士尼黑科技:爬墙机器人 VertiGo
12 月 30 日,迪士尼研发出的一款爬墙机器人曝光了一段有趣的视频.从视频里可看出这个机器人碰到墙壁时迅速地作出反应爬了上去. 据了解,这个爬墙机器人名叫 VertiGo,由迪士尼研究中心和苏黎世联 ...
- Android黑科技,读取用户短信+修改系统短信数据库
安卓系统比起ios系统最大的缺点,相信大家都知道,就是系统安全问题.这篇博客就秀一波“黑科技”. 读取用户短信 Android应用能读取用户手机上的短信,相信已经不是什么新鲜事,比如我们收到的短信验证 ...
- localStorage的黑科技-js和css缓存机制
一.发现黑科技的起因 今天在微信公众号看到一篇技术博文,想用印象笔记收藏,所以发送了文章链接到pc上.然后习惯性地打开控制台,看看源码,想了解下最近微信用了什么新技术. 呵呵,以下勾起了我侦探的欲 ...
- iOS 关于UITableView的黑科技
UITableView是我们最常用的控件了,今天我就来介绍一些关于UITableView的黑科技和一些注意的地方. 1.修改左滑删除按钮的高度 左滑删除这是iOS最先发明的,之后安卓开始模仿. ...
- Cnblogs关于嵌入js和css的一些黑科技
#pong .spoiler{cursor:none;display:inline-block;line-height:1.5;}sup{cursor:help;color:#3BA03B;} Pon ...
- 微软黑科技强力注入,.NET C#全面支持人工智能
微软黑科技强力注入,.NET C#全面支持人工智能,AI编程领域开始C#.Py--百花齐放 就像武侠小说中,一个普通人突然得到绝世高手的几十年内力注入,招式还没学,一身内力有点方 Introducin ...
随机推荐
- JAVA的初始化顺序(续)
JAVA在创建对象之前,是先加载类,然后再创建对象. 加载类时,会加载静态的成员变量,包括父类的静态成员变量[先加载父类,再加载子类]. 一. 静态成员变量的初始化 package com.cnbl ...
- MySQL prepare 原理
Prepare的好处 Prepare SQL产生的原因.首先从mysql服务器执行sql的过程开始讲起,SQL执行过程包括以下阶段 词法分析->语法分析->语义分析->执行计划优化 ...
- SQLServer 2012之AlwaysOn —— 指定数据同步链路,消除网络抖动导致的提交延迟问题
事件起因:近期有研发反应,某数据库从08切换到12环境后,不定期出现写操作提交延迟的问题: 事件分析:在排除了系统资源争用等问题后,初步分析可能由于网络抖动导致同步模式alwayson节点经常出现会话 ...
- CSDN 分糖果算法的思路和求助
昨天晚上 在csdn上做了一道分糖果的题目,我自个测的是没有问题,但是提交答案后,老失败,提示 你的程序正常运行并输出了结果,但是答案错误你的程序输出结果与测试数据中的输出结果不符 我先把自个思路说一 ...
- http://www.cnbeta.com/articles/306769.htm
事实上,很少有方法可以帮你做到.有些人可能会想到试着把Vim打造成C语言IDE的,比如c.vim:也有把Vim集成到Eclipse里的Eclim .但是我想要告诉你的是一个更加通用的,只用插件实现的方 ...
- Dynamic CRM 2013学习笔记(二十二)插件里调用WCF服务
1. 添加service: 2.调用WCF BasicHttpBinding myBinding = new BasicHttpBinding(); myBinding.Name = &q ...
- C++11 并发指南系列
本系列文章主要介绍 C++11 并发编程,计划分为 9 章介绍 C++11 的并发和多线程编程,分别如下: C++11 并发指南一(C++11 多线程初探)(本章计划 1-2 篇,已完成 1 篇) C ...
- Mac 快捷键整理
Mac 快捷键整理 文本编辑 适用于文本编辑器,浏览器等 跳到页首 cmd + ↑ 类似windows下的 ctrl + home 跳到页尾 cmd + ↓ 类似windows下的 ctrl + en ...
- Java中的Set, List, Map漫谈
在编程语言中,集合是指代表一组对象的对象.Java平台专门有一个集合框架(Collections Framework).集合框架是指表示和操作集合的统一架构,隔离了集合的操作和实现细节. 集合框架中的 ...
- Mac系统下lipo, ar, nm等工具的使用简介
引言 开发第三方库时, 如果没有进行特殊处理, 很容易把其他第三方库的符号暴露出来, 导致链接时产生符号重复. 如下图所示 如果用户链接了其他版本的libjpeg, 会因为入口地址不正确让程序直接崩溃 ...