1、C++里的钻石结构
class A { };
class B : virtual public A { };
class C : virtual public A { };
class D : public B, public C { };
2、long和float都是用四个字节表示
3、关键字volatile的作用
一个定义为volatile的变量是说这变量可能会被意想不到的改变,这样,编译器就不会去假设这个变量的值了。精确的说就是,优化器在用到这个变量时必须每次都小心的重新读取这个变量的值,而不是保存在寄存器里的备份。
4、抽象类和接口的区别
抽象类是特殊的类,只是不能被实例化(将定义了纯虚函数的类称为抽象类);除此之外,抽象类具有类的其它特性;重要的是抽象类可以包含抽象类和抽象方法,这是普通类所不能的,但同时也可以包含普通的方法。抽象方法只能声明于抽象类中,且不包含任何实现,派生类必须覆盖它们。另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖,如果不覆盖,则其派生类必须覆盖它们。虽然不能定义抽象类的实例,但是可以定义它的指针,这正是抽象类实现接口的重点所在。
接口是一个概念,它在C++中用抽象类实现,在C#和Java中用interface来实现。
接口是引用类型的,类似于类,和抽象类的相似之处有三点。
1)不能实例化
2)包含未实现的方法和声明
3)派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法,包括其它成员)
另外,接口有如下特性:
接口除了可以包含方法之外,还可以包含属性、索引器、事件,而这些成员都被定义为公有的。除此之外,不能包含其它的任何成员,例如:常量、域、构造函数、析构函数,静态成员。
5、sizeof和strlen的区别
sizeof是一种单目运算符,而strlen是一个函数。
sizeof的操作数可以是数据类型、函数、变量、表达式
strlen的应用则不像sizeof那么广泛,参数必须是char *型的指针
6、什么是野指针,什么情况下会产生野指针,如何避免?
野指针是指向不可用内存区域的指针。通常对野指针进行操作的话,会产生不可预知的错误。
野指针的产生有三种情况:
1)指针变量未被初始化
2)动态内存中指针被free或delete释放后没有被置为null
3)指针操作超越了变量的作用范围
避免:我们需要将指针初始化为NULL,用完后也为其赋值NULL
拷贝构造时防止浅拷贝
7、面向对象的三个要素五个原则
三要素:继承封装多态
封装:将抽象得到的数据和方法相结合,形成一个有机整体,使用者不必了解具体的实现细节,而只要通过外部接口,一特定的访问权限来使用类的成员
继承:继承可以使子类具有父类的各种属性和方法,而不需要编写相同的代码
多态:不同的对象调用相同的函数会产生不同的效果,允许将子类类型的指针赋给父类类型的指针
五元则:单一职责原则
应该有且仅有一个原因引起类的变更
开放封闭原则
对扩展开放,对修改封闭
里氏替换原则
子类可以扩展父类的功能,但不能改变父类的功能
依赖倒置原则
对抽象进行编程,不要对实现进行编程,这样就降低了业务与实现模块间的耦合
接口隔离原则
使用多个专门的接口总比使用一个单一的接口好
8、与类相关的sizeof操作
定义一个 空的类,没有任何的成员变量和成员函数,对该类型求sieof,得到的结果是多少?
答案是1,空类型的实例中不包含任何信息,本来求sizeof应该是0,但是当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占用多少内存,由编译器决定。Visual Studio中每个空类型的实例占用1字节的空间。
如果在该类型中添加一个构造函数和析构函数,再对该类型求sizeof,得到的结果又是多少?
和前面一样,还是1。调用构造函数和析构函数只需要知道函数的地址即可,而这些函数的地址只与类型相关,而与类型的实例无关,编译器也不会因为这两个函数而在实例内添加额外的信息。
那如果把析构函数标记为 虚函数呢?
C++的编译器一旦发现一个类型中有虚拟函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。在32位机器上,一个指针占4个字节的空间。
9、不允许复制构造函数传值
如果允许复制构造函数传值,就会在复制构造函数内调用复制构造函数,就会形成无休止的递归调用从而导致栈溢出