ReadNote

编译过程

C   ReadNote - 图1

  • 预编译:宏替代 -I
  • 编译:将预处理后的程序转成特定的汇编代码 -S
  • 汇编:将汇编代码转换成机器码 -as
  • 链接:将多个目标文以及库文件等链接成可执行文件

    data share and protect

namespace 作用域问题

  • 每个源文件的匿名命名空间是彼此不同的,在一个源文件中没有办法访问其他源文件的匿名命名空间。[page 149]
  • 具有命名空间作用域的变量也叫全局变量;

static 局部可见

  1. void fun(){
  2. static int a=5;
  3. int b=6;
  4. return;
  5. }
  6. int main(){
  7. int a = 3;
  8. cout<<a<<endl;
  9. return 0;
  10. }
  11. output:3

对象的复制

  1. 构造参数一致重新构造;
  2. 直接ob1 = ob2;
  3. 通过默认拷贝函数来复制,Object o1(10); Object o2(o1);(浅赋值)

构造函数

构造时可以将参数通过参数列表传递;

类型转换

类型转换包括显式和隐式,很多代码不经意的用到了隐式转换;

  • 如果不希望发生隐式转换,可以在构造函数前加上“explicit”;

静态数据成员

采用static定义静态数据成员,为该类的所有对象所共有;
它不属于任何对象,可以使用 类名::标识符 去初始化(除了初始化,外部都不可直接访问);

类的友元

  • 友元函数:虽然不是本类的成员函数,但可以通过对象名访问类的私有或保护成员;
  • 友元类:若a为b的友元类,则a的所有成员函数都是b的友元函数,都可以访问b的保护或私有成员;声明语法如下
  1. class b{
  2. ...
  3. friend class a; // 声明a为b的友元类
  4. ...
  5. }
  • 友元关系是单向的、不能传递的也不能继承的;

const

  1. const是函数的一个组成部分,在定义时也要加上;
  2. 如果一个对象被声明为常对象,那么只能调用它的常成员函数;
  3. 常成员函数不能更新目的对象的成员变量,也不能调用其他成员函数;
  4. const可以用于对重载函数的区分;eg:void a(); void a() const;

多文件管理和预编译

  • 局部变量尽量不用static,应当将不希望被其他编译单元引用的函数和变量放到匿名命名空间里;
  • using namespace 尽量不要放在头文件里;
  • 预编译指令 #include #define #undef;

数组、指针和字符串

数组

  • 二维数组一般按行优先存储;
  • 二维数组的下表可以不用显式,eg:int a[2][3] <=> int a[][3]

指针

  • 变量的引用
    • &出现在变量声明语句中位于被声明变量的左边时,表示的是引用;
      eg:int &rf;
    • &在给变量赋初值时出现在等号右边或在执行语句中作为一元运算符出现时,表示取对象的地址。
      eg:
      1. int a,b;
      2. int * pa, *pb = &b;
      3. pa = &a;
  • 指向常量的指针定义后,不可以改变其指向的对象的值,但能改变其指向;const int * p = &a;
  • 指针型的常量指针本身不能改变;int * const p = &a;
  • void * 型指针可以存储任何类型数据,经过显式转换就可以访问指向的内容;
    eg:

    1. void * pv;
    2. int i=5;
    3. pv = & i;
    4. int * print = static_cast<int *>(pv);
    5. cout<< "* print = "<<*print<<endl;
  • 指针型函数(p211)专门存放函数代码首地址;
    声明语法:数据类型 (* 函数指针名)(形参列表)
    赋值方式:函数指针名 = 函数名

  • 对象访问成员的方式有两个:
    1. 对象命访问:a.x
    2. 指针访问 :p = & a; p->x
  • this指针是一个隐含于每个类的非静态成员函数中的特殊指针(包含构造和析构函数),它用于指向正在被成员函数操作的对象;
  • 用new分配的内存,必须用delete释放,否则会内存泄漏;
  • new t和new t()的区别在于是否有自定义的构造函数;new int []可以生成数组;
  • new 数据类型 (初始化参数列表);
  • 对于基本类型,如果不希望分配内存赋值,new 数据类型;保留括号表示用0初始化对象;

    在new建立一个对象时: 如果该类存在用户自定义的默认构造函数,则new T和new T()都是调用默认构造函数; 如果为定义默认构造函数,则new T调用默认构造函数,而new T ()除此之外还将所有的基本数据类型和指针类型成员用0赋值;

  • new 建立的数组必须用delete[]删除;

  • 通过类成员访问数组,可以使用assert检查越界问题;( assert only be used in debug mode )
  • 动态创建的意义在于能在局部作用域建立一个与程序生存周期相同的存储堆,如果用静态创建,则会被创建到运行栈里;
  • vector是一个类模板,用于定义动态数组;
  • vector <类型名> 对象名(数组长度); vector定义的所有对象都会被初始化(调用构造换数);访问用对象名[下标];
  • cin >> 的分隔符是<空格>;
  • c++中 . 和 -> 主要是用法上的不同。
    1、A.B则A为对象或者结构体;
    2、A->B则A为指针,->是成员提取,A->B是提取A中的成员B,A只能是指向类、结构、联合的指针;
  • 共享指针;

引用和指针

引用是高层机制,指针是底层机制;二者殊途同归,引用的实现需要借助地址,差异主要是语言形式上的;

堆栈

  • 内存格局通常分为四个区
    全局数据区:存放全局变量,静态数据,常量;
    代码区:存放所有的程序代码;
    栈区:存放为运行而分配的局部变量,参数,返回数据,返回地址等;
    堆区:即自由存储区。
  • 栈, 是硬件, 主要作用表现为一种数据结构, 是只能在一端插入和删除数据的特殊线性表。
  • 堆, 是一种动态存储结构, 实际上就是数据段中的自由存储区, 它是C语言中使用的一种名称, 常常用于存储、 分配动态数据。堆中存入的数据地址向增加方向变动。
  • 堆可以不断进行分配直到没有堆空间为止, 也可以随时进行释放、 再分配, 不存在顺序问题。
    堆内存的分配常通过malloc() 、 calloc() 、 realloc() 三个函数来实现。而堆内存的释放则使用free() 函数。(for c process)
    堆和栈在使用时“生长”方向相反, 栈向低地址方向“生长”, 而堆向高地址方向“生长”。
  • 为什么要有堆栈?
    栈:为了存放运行时的局部变量,参数,返回数据,返回地址等。
    堆:栈的性能非常高,但是对于所有的变量来说还不太灵活,而且变量的生命周期必须嵌套。
    通常我们希望使用一种方法分配内存来存储数据,并且方法退出后很长一段时间内数据仍然可以使用。此时就要用到堆(托管堆)。
  • 堆(Heap)是应用程序在运行的时候请求操作系统分配给自己内存,一般是申请/给予的过程。
    由于从操作系统管理的内存分配所以在分配和销毁时都要占用时间,所以用堆的效率低的多!但是堆的好处是可以做的很大,C/C++对分配的Heap是不初始化的。

继承与派生

派生类的生成过程

  1. 吸收基类成员:继承基类的构造和析构以外的非静态成员;
  2. 改造基类成员:继承方式和对基类成员的覆盖隐藏;
  3. 添加新成员;

继承理解

首先,继承只是继承了结构,这要做的目地是更加灵活的描述现实逻辑;如B继承了A,B只能通过B的对象访问A定义的成员,不能通过A的对象访问;
其次,public相当于给外界接口,protected用法上和privated是一样的,区别是它可以继承;privated成员只能在本体内访问并且不能继承;
最后,继承方式对privated成员没有影响(继承不了privated成员),而protected方式将puliced成员改为protected;privated方式将所有能继承成员变为私有成员;publiced方式改变成员属性;

虚基类

虚继承和虚基类详解
虚基类的构造

继承总结

  • 类型兼容规则是在需要基类对象的任何地方都可以使用public派生类的对象来替代,替代后只能使用继承下来的成员;例如P263(大替小)
  • 作用域分辨符的隐藏规则:在有成员二义性的作用域,使用分辨符可以消除二义性;
  • 只有在相同作用域定义的函数才能重载;
  • 使用在成员函数的实现里,使用using可以让成员函数和using作用域的内容重载;
  • 类的组合,即一个类内嵌其他类的对象;
  • 虚基类是用来解决多个类继承之间有重复分配空间的问题的;
  • 兼容规则的内存布局问题;

多态性

概念

多态性是指同样的消息被不同类型的对象接收时导致不同的行为;
多态4种类型:重载多态,强制多态,包含多态和参数多态;
重载分为编译重载和实现重载;
函数重载和运算符重载是编译时多态的基础;

运算符重载

  • 临时对象语法:return 类名(参数);
    eg:

    1. return complex(real+a.real,imag + a.imag);
  • 声明时的区别:
    | 前置单目运算 | 后置单目运算 | | —- | —- | | 重载函数没有形参 | 重载函数有一个形参 |

虚函数(理解)

参考:https://blog.csdn.net/haoel/article/details/1948051

  • 虚函数是动态绑定的基础,虚函数允许是非静态成员函数,经过派生后可以实现运行过程中的多态;
  • C++中的虚函数的作用主要是实现了多态的机制,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。
    所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。
  • 虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table;
    在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。它就像一个地图一样,指明了实际所应该调用的函数。C   ReadNote - 图2

虚函数(使用)

使用条件:

  1. 满足类型兼容规则;
  2. 要声明虚函数;
  3. 要由成员函数调用或通过指针引用访问虚函数,如果用对象名访问,绑定在编译时就完成了(静态绑定),用不到虚函数表;
  1. //对象访问
  2. void fun(base1 p){p.display();}
  3. int main(int argc, char const *argv[])
  4. {
  5. base1 b1;
  6. base2 b2;
  7. derived d;
  8. fun(b1);
  9. fun(b2);
  10. fun(d);
  11. return 0;
  12. }
  13. output:
  14. base1::display//类型兼容
  15. base1::display
  16. base1::display
  17. //引用访问
  18. void fun(base1 * p){ p->display();}
  19. int main(int argc, char const *argv[]){
  20. base1 b1;//b1 b2都是对象名而已
  21. base2 b2;
  22. derived d;
  23. fun(&b1);
  24. fun(&b2);
  25. fun(&d);
  26. return 0;
  27. }
  28. output:
  29. base1::display
  30. base2::display
  31. derived::display
  32. //src stay in test9

纯虚函数

test:test10

  • 带有纯虚函数的类是抽象类,只能继承,不能实例化;
  • 若派生类未给出纯虚函数的实现,则派生类仍是抽象类;
  • 抽象类可以定义指针或引用去访问派生类的对象,这种访问是具有多态性的;

多态类型与非多态类型

  • 设计多态类型的一个重要原则:把多态类型的析构函数设定为虚函数;
  • 对非多态类型的公有继承应当慎重,而且一般没太大必要;
  • c++ 获取类型名称:typeid;test10/typeid

群体

函数模板

  • C++最重要的特性之一就是代码重用;
  • 通过模板可以实现参数化多态;
  • 编译器以函数模板生成一个函数的过程为函数模板的实例化,函数为该模板的实例
  • 在定义类模板或者函数模板时,typename 和 class 关键字都可以用于指定模板参数中的类型。
    typename 另外一个作用为:使用嵌套依赖类型(nested depended name);
    这个时候 typename 的作用就是告诉 c++ 编译器,typename 后面的字符串为一个类型名称,而不是成员函数或者成员变量; ```cpp class MyArray { public: typedef int LengthType; ….. }

template void MyMethod( T myarr ) { typedef typename T::LengthType LengthType; LengthType length = myarr.GetLength; }

  1. - typedef #define 区别 在上述的变量定义中,s1s2s3都被定义为 char_ 类型;但是s4则定义成了 char 类型,而不是我们所预期的指针变量 char_,这是因为 #define 只做简单的字符串替换,替换后的相关代码等同于为: 而使用 typedef char* 定义了新类型 pStr1 后,相关代码等同于为:
  2. ```c
  3. typedef char* pStr1;
  4. #define pStr2 char* 
  5. pStr1 s1, s2;
  6. pStr2 s3, s4;
  1. char* s3, s4;
  1. char *s3, *s4;
  • 函数模板本身在编译是不生成目标代码;只有实例化是才生成目标代码;
  • 被多个文件引用的函数模板应将将函数也一并放入头文件中;
  • 函数指针也只能指向模板实例,而不是模板本身;

类模板

重载“=”和“[]”时,返回类型都是对对象的引用;


总结

  • 类模板不是类;实例化的类之间也不能兼容;
  • 实例化是参数类型必须显式指定;
  • 模板的特化是指一个模板在某些特定参数下提供的特殊定义;
  • 特化类模板应该放在源文件而非头文件中(p390);
  • 偏特化;
  • C++不允许将函数模板偏特化,但函数模板可以重载;
  • 模板元编程是指在模板实例化的同时利用编译器完成一些计算任务;从而提程序的运行效率;(p392)

泛程序和标准模板库

迭代器

  • 对于输入迭代器而言,p1==p2不能保证++p1==++p2,更不能保证(+p1)==(++p2); 因此输入迭代器只适合做只需要遍历一次的算法输入;
  • vector::begin():Return iterator to beginning
    Returns an iterator pointing to the first element in the vector.
    Notice that, unlike member vector::front, which returns a reference to the first element, this function returns a random access iterator pointing to it.
    If the container is empty, the returned iterator value shall not be dereferenced.
    ——C++ reference

面经总结

经典C++笔试题目100例,接近实际,值得一看!

https://blog.csdn.net/weixin_41168353/article/details/80083861

C 和 C++ 中 struct 有什么区别?(J129)

Protection行为 能否定义函数
C 可以
C++ 可以,默认是public

C++中的 struct 和 class 有什么区别?

  1. 默认继承权限。如果不明确指定,来自class的继承按照private继承处理,来自struct的继承按照public继承处理;
  2. 成员的默认访问权限。class的成员默认是private权限,struct默认是public权限。

    除了这两点,class和struct基本就是一个东西。语法上没有任何其它区别。

如何判断一段程序是由C 编译程序还是由C++编译程序编译的?

  1. #ifdef __cplusplus
  2. cout<<"c++"<<endl;
  3. #else
  4. printf("c /n");
  5. #endif

int id[sizeof(unsigned long)]; 这个对吗?为什么?

正确 这个 sizeof是编译时运算符,编译时就确定了 可以看成和机器有关的常量。

C++函数中值的传递方式有哪几种?

C++函数的三种传递方式为:值传递、指针传递和引用传递。

对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?

C用宏定义,C++用 inline(內联);

引用与指针有什么区别?

1) 引用必须被初始化,指针不必。
2) 引用初始化以后不能被改变,指针可以改变所指的对象。
3) 不存在指向空值的引用,但是存在指向空值的指针。


C++中 virtual 与 inline 的含义分别是什么?

在基类成员函数的声明前加上virtual关键字,意味着将该成员函数声明为虚函数。
inline与函数的定义体放在一起,使该函数称为内联。inline是一种用于实现的关键字,而不是用于声明的关键字。
虚函数的特点:
如果希望派生类能够重新定义基类的方法,则在基类中将该方法定义为虚方法,这样可以启用动态联编。
内联函数的特点:
使用内联函数的目的是为了提高函数的运行效率。
内联函数体的代码不能过长,因为内联函数省去调用函数的时间是以代码膨胀为代价的。
内联函数不能包含循环语句,因为执行循环语句要比调用函数的开销大。

內联函数补充

1)inline放在头文件中
inline是加在实现上,就算加在声明上,编译器也会忽略掉。内联展开是在编译时进行的,只有链接的时候源文件之间才有关系。所以内联要想跨源文件必须把实现写在头文件里。如果一个inline函数会在多个源文件中被用到,那么必须把它定义在头文件中。
最佳实践是:仅把inline关键字放在类外部函数的定义前。
inline函数的特征是在调用的地方插入相应函数的代码,所以编译之后的目标文件里是没有inline函数体的,因为在要调用的地方它都已经用相应的语句替换掉了(当然这只限于内联成功的情况)。如果我们将inline函数写在cpp文件里,但是绝大多数情况下,在我们用第三方类库的时候,我们只有头文件和目标文件(没有cpp文件),当你调用那个内联函数时,编译器没办法找到它。所以说将inline函数写在cpp文件中是没什么用的。
2)编译器并不承诺将带有online关键字的函数变为內联函数;不带online也有可能被优化成內联函数;
3)在类的声明里直接给出函数实现,隐含的是将该函数变为內联函数;
4)关键字inline必须与函数的定义体放在一起,才能使函数成为内联函数,仅仅将inline放在函数声明前面不起作用;
5)inline只适合函数体内代码比较简单的函数使用,不能包含复杂的结构控制语句,例如while、switch,并且内联函数本身不能是直接递归函数(函数内部调用自己的函数)。
6)static和inline联合使用
static是静态修饰符,由其关键字修饰的变量会保存到全局数据区,对于普通的局部变量或者全局变量,都是由系统自动分配内存的,并且当变量离开作用域的时候释放掉,而使用static关键字来修饰,只有当程序结束时候才会释放掉,使用static inline修饰时,函数仅在文件内部可见,不会污染命名空间,另外,函数在运行过程中也会分配内存空间,但是由于static的存在,就和修饰变量类似,它只会开辟一块内存空间。

函数 assert 的用法?

断言assert是仅在debug版本起作用的宏,用于检查“不应该“发生的情况。
程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。

const 与 #define 的比较 ,const有什么优点?

(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应) 。
(2)有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。

有了 malloc/free 为什么还要 new/delete ?

malloc 与 free 是 C++/C 语言的标准库函数,new/delete 是 C++的运算符。它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free。
因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符 new,以及一个能完成清理与释放内存工作的运算符 delete。
注意 new/delete 不是库函数。

C++是不是类型安全的?

不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。

const 符号常量

(1)const char p
(2)char const
p
(3)char * const p
说明上面三种描述的区别:
(1) p是一个指向const char的指针,p是可以改变指向的,但是p指向的值是不能改变的;
(2) p指向的恰好是一个指向const的char的普通指针;
(3) p是一个指针,这个指针是指向char的const指针。
(1)和(2)的定义是一样的。

用C++写个程序,如何判断一个操作系统是16位还是32位的?不能用 sizeof() 函数。

  1. cout<<sizeof(void*)*8<<"bit"<<endl;
  2. //实际上测出来的是编译器的位数 gcc-v

linux cmd:getconf LONG_BIT 或者 uname -a

识别函数或指针

void ( (fp1)(int))[10];
float (
( fp2)(int,int,int))(int);
int (
( fp3)())10;
分别表示什么意思?
1、void
( (fp1)(int))[10]; fp1是一个指针,指向一个函数,这个函数的参数为int型,函数的返回值是一个指针,这个指针指向一个数组,
这个数组有10个元素,每个元素是一个void型指针。
2、float (
( fp2)(int,int,int))(int); fp2是一个指针,指向一个函数,这个函数的参数为3个int型,函数的返回值是一个指针,这个指针指向一个函数,
这个函数的参数为int型,函数的返回值是float型。
3、int (
( * fp3)())10; fp3是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是一个指针,这个指针指向一个数组,
这个数组有10个元素,每个元素是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是int型。

多态类中的虚函数表是 Compile-Time,还是 Run-Time 时建立的?

虚拟函数表是在编译期就建立了,各个虚拟函数这时被组织成了一个虚拟函数的入口地址的数组.而对象的隐藏成员—虚拟函数表指针是在运行期—也就是构造函数被调用时进行初始化的,这是实现多态的关键。

错误的转义字符是?
A、’\091’
B、’\‘
C、’\0’ D.’\’’
【标准答案】A

转义字符补充:转义字符以\或者\x开头,以\开头表示后跟八进制形式的编码值,以\x开头表示后跟十六进制形式的编码值。对于转义字符来说,只能使用八进制或者十六进制。
转义字符的初衷是用于 ASCII 编码,所以它的取值范围有限:

  • 八进制形式的转义字符最多后跟三个数字,也即\ddd,最大取值是\177;
  • 十六进制形式的转义字符最多后跟两个数字,也即\xdd,最大取值是\x7f。

内存的分配方式有几种?

一、从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量。
二、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效
率很高,但是分配的内存容量有限。
三、从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们
决定,使用非常灵活,但问题也最多。

float a,b,c , 问等式 (a+b)+c==(b+a)+c 和 (a+b)+c==(a+c)+b 能否成立?

两者都不行。在比较float或double时,不能简单地比较。由于计算误差,相等的概率很低。应判断两数之差是否落在区间(-e,e)内。
这个e应比浮点数的精度大一个数量级。

由于计算机中采用的是有限位的二进制编码,所以浮点数在计算机中的存储不总是精确的。例如,在经过大量计算后,一个浮点型的数3.14在计算机中可能就存储成3.140000000001,也有可能存储成3.1499999999999,这种情况下会对比较操作带来极大的干扰,所以我们需要引入一个极小数 eps 来对这种误差进行修

全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?

使用方式不同:
通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用;分配在栈区。
操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面 。

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由os回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放。 4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放。 5、程序代码区—存放函数体的二进制代码。

In C++, what does “explicit” mean?

C++中的 explicit 关键字用来修饰类的构造函数,表明该构造函数是显式的,在某些情况下,我们要求类的使用者必须显示调用类的构造函数时就需要使用 explicit,反之默认类型转换可能会造成无法预期的问题。

const关键字?有哪些作用?

const关键字至少有下列n个作用:
(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。

是不是一个父类写了一个virtual 函数,如果子类覆盖它的函数不加virtual ,也能实现多态?

能,默认的

面向对象的三个基本特征,并简单叙述之?

  1. 封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private, protected, public)
    2. 继承:广义的继承有三种实现形式:
    实现继承(指使用基类的属性和方法而无需额外编码的能力)、
    可视继承(子窗体使用父窗体的外观和实现代码)、
    接口继承(仅使用属性和方法,实现滞后到子类实现)。
    前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。
    3. 多态:是将父对象设置成为和一个或更多的与他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
    简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

重载(overload)、重写(override,有的书也叫做“覆盖”)、重定义(redefinition)的区别?

重载 同一名字空间 是指允许存在多个同名函数,而这些函数的参数表不同。
重定义/隐藏 不同名字空间。用于继承,派生类与基类的函数同名,屏蔽基类的函数
重写/覆盖 不同名字空间。用于继承,子类重新定义父类虚函数的方法

C++里面是不是所有的动作都是main()引起的?如果不是,请举例。

比如全局变量的初始化,就不是由 main 函数引起的。举例:

  1. class A{
  2. };
  3. A a; //a的构造函数限执行
  4. int main() {
  5. }

内联函数在编译时是否做参数类型检查

内联函数要做参数类型检查, 这是内联函数跟宏相比的优势。

请讲一讲析构函数和虚函数的用法和作用?

析构函数是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载,只有在类对象的生命期结束的时候,由系统自动调用。
有适放内存空间的作用。
虚函数是C++多态的一种表现, 使用虚函数,我们可以灵活的进行动态绑定,当然是以一定的开销为代价。

关于new 和 malloc

malloc是库函数,不在编译器控制范围之内;new是运算符,在编译器控制范围之内。
调用malloc时,从堆中申请内存;调用new时,从堆中申请内存并为内存调用构造函数。

构造函数为什么一般不定义为虚函数?而析构函数一般写成虚函数的原因 ?

1、构造函数不能声明为虚函数
1)因为创建一个对象时需要确定对象的类型,而虚函数是在运行时确定其类型的。而在构造一个对象时,由于对象还未创建成功,编译器无法知道对象的实际类型,是类本身还是类的派生类等等
2)虚函数的调用需要虚函数表指针,而该指针存放在对象的内存空间中;若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址用来调用虚函数即构造函数了

2、析构函数最好声明为虚函数
首先析构函数可以为虚函数,当析构一个指向派生类的基类指针时,最好将基类的析构函数声明为虚函数,否则可以存在内存泄露的问题。
如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除指向派生类的基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。


在C++中有没有纯虚构造函数?

构造函数不能是虚的。只能有虚的析构函数。

对调用的虚函数和模板类都进行迟后编译

在 C++的一个类中声明一个 static 成员变量有没有用?

在C++类的成员变量被声明为 static(称为静态成员变量),意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,
也就是说不管创建多少对象,static修饰的变量只占有一块内存。其修改值为该类的其它所有实例所见;而类的静态成员函数也只能访问静态成员(变量或函数)。static是加了访问控制的全局变量,不被继承。

C++中为什么用模板类?

(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型

函数模板与类模板有什么区别?

函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。

请你谈谈你在类中如何使用 const 的。

有时我们希望某些常量只在类中有效。由于#define 定义的宏常量是全局的,不能达到目的,于是想当然地觉得应该用 const 修饰数据成员来实现。
const 数据成员的确是存在的,但其含义却不是我们所期望的。const 数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其 const 数据成员的值可以不同。 不能在类声明中初始化 const 数据成员。
const 数据成员的初始化只能在类构造函数的初始化表中进行。

函数重载,我们靠什么来区分调用的那个函数?靠返回值判断可以不可以?

如果同名函数的参数不同(包括类型、顺序不同) ,那么容易区别出它们是不同的。如果同名函数仅仅是返回值类型不同,有时可以区分,有时却不能。例如:
void Function(void);
int Function (void);
上述两个函数,第一个没有返回值,第二个的返回值是 int 类型。如果这样调用函数:
int x = Function ();
则可以判断出 Function 是第二个函数。问题是在 C++/C 程序中,我们可以忽略函数的返回值。在这种情况下,编译器和程序员都不知道哪个 Function 函数被调用。 所以只能靠参数而不能靠返回值类型的不同来区分重载函数。

写出运行结果(sizeof):

{
// test1 char str[] = “world”;
cout << sizeof(str) << “: “;
char p = str;
cout << sizeof(p) << “: “;
char i = 10;
cout << sizeof(i) << “: “;
void
pp = malloc(10);
cout << sizeof(pp) << endl;
}
6:4:1:4
指针占4个字节;void * 占一个字节,但分配好类型时占4个。