IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++一些面试时的基础知识复习 -> 正文阅读

[C++知识库]c++一些面试时的基础知识复习

1、指针和引用的区别
(1)指针有自己的一块空间,而引用只是一个别名
(2)sizeof一个指针大小为4字节(32位,64位的话为8字节),而sizeof引用则为引用对象的大小
(3)指针可以被初始化为NULL,而引用必须被初始化,且必须是一个已有的对象
(4)作为参数传递时,指针需要解引用才可以对对象进行操作,而直接对引用的修改都会改变所引用的对象
(5)指针可以指向其他对象,而引用只能是一个对象的引用,不能被改变
(6)指针可以是多级的,而引用没有分级
(7)如果返回动态分配内存的对象或者内存,必须使用指针,引用可能引起内存泄露

关于引用:
函数的返回值用于初始化在调用函数时创建的临时对象。
如果不是引用,会将函数返回值复制给临时对象
当返回非引用类型,其返回值可以是局部对象,也可以是求解表达式的结果
返回引用类型,没有复制返回值,返回的是对象本身,千万不要返回局部对象的引用,不要返回指向局部对象的指针
返回引用时,要求在函数的参数中,包含有以引用方式或指针方式存在的,需要被返回的参数,同时可以利用全局变量,作为返回。还可以返回对象(不是在当前返回引用这个函数中实例化的对象),原因是类成员函数的参数表中都有一个隐含的this指针,指向当前对象的地址。
返回值为引用时,返回的是地址。
返回值不是引用时,编译器会给返回值分配一块内存。
返回引用,调用程序将直接访问返回值。
通常引用将指向传递给函数的引用,因此调用函数实际上是直接访问自己的一个变量
返回一个对象时,一般用引用作为返回值。

引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针,或者指针引用。

可以建立指针变量的引用如
int i=5;
int *p=&i;
int * &pt=p;//建立指针变量p的引用pt

2、堆和栈的区别
(1)堆栈空间分配区别:
栈:由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似数据结构中的栈;
堆:一般由程序员分配释放,若程序员不释放,程序结束时可能由os回收,分配方式倒是类似于链表。
(2)堆栈缓存方式的区别
栈:由系统自动分配,速度较快,连续的空间,获得的空间较小。是内存中存储值类型的,大小为2M(window,linux下默认为8M,可以更改),超出则会报错,内存溢出
堆:速度比较慢,不连续的空间,获得的空间较大。内存中,存储的是引用数据类型,引用数据类型无法确定大小,堆实际上是一个在内存中使用到内存中零散空间的链表结构的存储空间,堆的大小由引用类型的大小直接决定,引用类型的大小的变化直接影响到堆的变化。
(3)堆栈数据结构上的区别
堆(数据结构):堆可以被看成是一颗树,如堆排序。
栈(数据结构):一种先进后出的数据结构。

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

3、new和delete是如何实现的,与malloc和free有什么异同
new操作符针对数据类型的处理,分为两种情况:
(1)简单数据类型(包括基本数据类型和不需要构造函数的类型)

  • 简单类型直接调用operator new分配内存
  • 可以通过new_handler来处理new失败的情况
  • new分配失败的时候不像malloc那样返回NULL,它直接抛出异常(bad_alloc)。要判断是否分配成功应该用异常捕获的机制;
    (2)复杂数据类型(需要由构造函数初始化对象)
    new复杂数据类型的时候先调用operator new,然后在分配的内存上调用构造函数。

delete也分为两种情况:
(1)简单数据类型(包括基本数据类型和不需要析构函数的类型)
delete简单数据类型默认只是调用free函数
(2)复杂数据类型(需要析构函数销毁的对象)
delete复杂数据类型先调用析构函数再调用operator delete。

与malloc和free的区别:
(1)属性上:new/delete是c++关键字,需要编译器支持。malloc/free是库函数,需要c的头文件的支持。
(2)参数:使用new操作符申请内存无需指定内存块大小,编译器会根据类型信息自行计算,而malloc则需显示地指出内存的尺寸。
(3)new操作符分配内存成功时,返回的是对象类型的指针,类型严格与类型匹配,故new是符合类型安全性的操作符。而malloc内存分配成功返回的是void *,需要通过类型转换将其转换为我们需要的类型。
(4)分配失败时,new抛出bad_alloc异常,而malloc返回NULL
(5)自定义类型,ew会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。 malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
(6)重载,c++允许重载new/delete操作符。而malloc为库函数不允许重载
(7)内存区域:new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。其中自由存储区为c++基于new操作符的一个抽象概念,凡是通过new进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统维护的一块特殊内存,用于程序内存的动态分配,c语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,new可以不位于堆中。

4、struct 和 class 的区别
(1)首先说一下C中的结构体和C++中的结构体的异同:
在这里插入图片描述

(2)C++中 struct 与 class 的区别:
内部成员变量及成员函数的默认访问属性:struct 默认防控属性是 public 的,而 class 默认的访问属性是private的
继承关系中默认访问属性的区别:在继承关系,struct 默认是 public 的,而 class 是 private
class这个关键字还可用于定义模板参数,就等同于 typename;而strcut不用与定义模板参数

5、define 和 const 的区别
(1)起作用的阶段: #define是在编译的预处理阶段起作用,而const是在编译、运行的时候起作用。
(2)作用的方式:const常量有数据类型,而宏常量没有数据类型,只是简单的字符串替换。编译器可以对前者进行类型安全检查。而对后者没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。
(3)存储的方式:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份,const比较节省空间,避免不必要的内存分配,提高效率。

6、在c++中 const 和 static 的用法
(1)static:
修饰全局变量:存储在静态存储区;未经初始化的全局静态变量自动初始化为 0;作用域为整个文件之内。
修饰局部变量:存储在静态存储;未经初始化的局部静态变量会被初始化为0;作用域为局部作用域,但离开作用域不被销毁。
修饰静态函数:静态函数只能在声明的文件中可见,不能被其他文件引用
修饰类的静态成员:在类中,静态成员可以实现多个对象之间的数据共享,静态成员是类的所有对象中共享的成员,而不属于某一个对象;类中的静态成员必须进行显示的初始化
修饰类的静态函数:静态函数同类的静态成员变量一个用法,都是属于一个类的方法。而且静态函数中只可以使用类的静态变量。
(2)const:
修成类成员:在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数; const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。
修饰类函数:该函数中所有变量均不可改变。

7、C++的顶层const和底层const
底层const是代表对象本身是一个常量(不可改变);
顶层const是代表指针的值是一个常量,而指针的值(即对象的地址)的内容可以改变(指向的不可改变);

8、拷贝初始化和直接初始化,初始化和赋值的区别
ClassTest ct1(“ab”); 这条语句属于直接初始化,它不需要调用复制构造函数,直接调用构造函数 ClassTest(const char *pc),所以当复制构造函数变为私有时,它还是能直接执行的。
ClassTest ct2 = “ab”; 这条语句为复制初始化,它首先调用构造函数 ClassTest(const char *pc) 函数创建一个临时对象,然后调用复制构造函数,把这个临时对象作为参数,构造对象ct2;所以当复制构造函数变为私有时,该语句不能编译通过。
ClassTest ct3 = ct1;这条语句为复制初始化,因为 ct1 本来已经存在,所以不需要调用相关的构造函数,而直接调用复制构造函数,把它值复制给对象 ct3;所以当复制构造函数变为私有时,该语句不能编译通过。
ClassTest ct4(ct1);这条语句为直接初始化,因为 ct1 本来已经存在,直接调用复制构造函数,生成对象 ct3 的副本对象 ct4。所以当复制构造函数变为私有时,该语句不能编译通过。
要点就是拷贝初始化和直接初始化调用的构造函数是不一样的,但是当类进行复制时,类会自动生成一个临时的对象,然后再进行拷贝初始化。

9、extern "C"的用法?
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。比如说你用C 开发了一个DLL 库,为了能够让C ++语言也能够调用你的DLL输出(Export)的函数,你需要用extern "C"来强制编译器不要修改你的函数名。

10、模板函数和模板类的特例化
引入的原因:编写单一的模板,它能适应大众化,使每种类型都具有相同的功能,但对于某种特定类型,如果要实现其特有的功能,单一模板就无法做到,这时就需要模板特例化。
定义:是对单一模板提供的一个特殊实例,它将一个或多个模板参数绑定到特定的类型或值上。

函数模板特例化:必须为原函数模板的每个模板参数都提供实参,且使用关键字template后跟一个空尖括号对<>,表明将原模板的所有模板参数提供实参。

template<typename T> //函数模板
int compare(const T &v1,const T &v2)
{
    if(v1 > v2) return -1;
    if(v2 > v1) return 1;
    return 0;
}
//模板特例化,满足针对字符串特定的比较,要提供所有实参,这里只有一个T
template<> 
int compare(const char* const &v1,const char* const &v2)
{
    return strcmp(p1,p2);
}

此处如果是compare(3,5),则调用普通的模板,若为compare(“hi”,”haha”)则调用特例化版本(因为这个cosnt char*相对于T,更匹配实参类型),注意,二者函数体的语句不一样了,实现不同功能。

类模板的部分特例化:不必为所有模板参数提供实参,可以指定一部分而非所有模板参数,一个类模板的部分特例化本身仍是一个模板,使用它时还必须为其特例化版本中未指定的模板参数提供实参。此功能就用于STL源码剖析中的traits编程。详见C++primer 628页的例子。(特例化时类名一定要和原来的模板相同,只是参数类型不同,按最佳匹配原则,那个最匹配,就用相应的模板)

template<typename T>class Foo
{
    void Bar();
    void Barst(T a)();
};
template<>
void Foo<int>::Bar()
{
    //进行int类型的特例化处理
}

Foo<string> fs;
Foo<int> fi;//使用特例化
fs.Bar();//使用的是普通模板,即Foo<string>::Bar()
fi.Bar();//特例化版本,执行Foo<int>::Bar()
//Foo<string>::Bar()和Foo<int>::Bar()功能不同

11、STL内存优化
STL内存管理使用二级内存配置器。
(1) 第一级配置器:
第一级配置器以malloc(),free(),realloc()等C函数执行实际的内存配置、释放、重新配置等操作,并且能在内存需求不被满足的时候,调用一个指定的函数。一级空间配置器分配的是大于128字节的空间,如果分配不成功,调用句柄释放一部分内存,如果还不能分配成功,抛出异常。

第一级配置器只是对malloc函数和free函数的简单封装,在allocate内调用malloc,在deallocate内调用free。同时第一级配置器的oom_malloc函数,用来处理malloc失败的情况。

(2) 第二级配置器:
第一级配置器直接调用malloc和free带来了几个问题:
内存分配/释放的效率低
当配置大量的小内存块时,会导致内存碎片比较严重
配置内存时,需要额外的部分空间存储内存块信息,所以配置大量的小内存块时,还会导致额外内存负担
如果分配的区块小于128bytes,则以内存池管理,第二级配置器维护了一个自由链表数组,每次需要分配内存时,直接从相应的链表上取出一个内存节点就完成工作,效率很高。

自由链表数组:自由链表数组其实就是个指针数组,数组中的每个指针元素指向一个链表的起始节点。数组大小为16,即维护了16个链表,链表的每个节点就是实际的内存块,相同链表上的内存块大小都相同,不同链表的内存块大小不同,从8一直到128。如下所示,obj为链表上的节点,free_list就是链表数组。

//自由链表union obj
{
          union obj *free_list_link; 
          char data[1];
};
//自由链表数组
static obj *volatile free_list[__NFREELISTS];

内存分配:allocate函数内先判断要分配的内存大小,若大于128字节,直接调用第一级配置器,否则根据要分配的内存大小从16个链表中选出一个链表,取出该链表的第一个节点。若相应的链表为空,则调用refill函数填充该链表。默认是取出20个数据块。

填充链表 refill:若allocate函数内要取出节点的链表为空,则会调用refill函数填充该链表。refill函数内会先调用chunk_alloc函数从内存池分配一大块内存,该内存大小默认为20个链表节点大小,当内存池的内存也不足时,返回的内存块节点数目会不足20个。接着refill的工作就是将这一大块内存分成20份相同大小的内存块,并将各内存块连接起来形成一个链表。

内存池:chunk_alloc函数内管理了一块内存池,当refill函数要填充链表时,就会调用chunk_alloc函数,从内存池取出相应的内存。

在chunk_alloc函数内首先判断内存池大小是否足够填充一个有20个节点的链表,若内存池足够大,则直接返回20个内存节点大小的内存块给refill;
若内存池大小无法满足20个内存节点的大小,但至少满足1个内存节点,则直接返回相应的内存节点大小的内存块给refill;
若内存池连1个内存节点大小的内存块都无法提供,则chunk_alloc函数会将内存池中那一点点的内存大小分配给其他合适的链表,然后去调用malloc函数分配的内存大小为所需的两倍。若malloc成功,则返回相应的内存大小给refill;若malloc失败,会先搜寻其他链表的可用的内存块,添加到内存池,然后递归调用chunk_alloc函数来分配内存,若其他链表也无内存块可用,则只能调用第一级空间配置器。

12、频繁对vector调用push_back()对性能的影响和原因
在一个vector的尾部之外的任何位置添加元素,都需要重新移动元素。而且,向一个vector添加元素可能引起整个对象存储空间的重新分配。重新分配一个对象的存储空间需要分配新的内存,并将元素从旧的空间移到新的空间。(哪有只push不pop的,偶尔某次空间不够重新分配这也算影响?)

13、C++ 重载和重写的区别
重载:是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。
重写:指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰(必须吗?不写也可以,只不过指向子类对象的基类指针调用的是基类的方法)。

14、C++中类的数据成员和成员函数内存分布情况

C++类内存布局图(成员函数和成员变量,分开讨论)

15、析构函数一般写成虚函数的原因?
先看下面这段程序

#include <iostream>
using namespace std;


class Person
{
public:
virtual ~Person()                    //加了virtual,讲析构函数声明为虚函数
{
   cout << "Person::~Person()" << endl;
}
};

class Student : public Person
{
public:
~Student()                                 // virtual可加可不加
{
   cout << "Student::~Student()" << endl;
}
};

int main()
{
Person *pt1 = new Person;
Person *pt2 = new Student;          // 用基类的指针指向子类
// Student *pt3 = new Person;     // 不能用子类指针指向基类,错误!
Student *pt4 = new Student;

delete pt1;
cout << "*********" << endl;
delete pt2;
cout << "*********" << endl;
//delete pt3;
//cout << "*********" << endl;
delete pt4;
cout << "*********" << endl;

return 0;
}
/*
运行结果:

Person::~Person()

Student::~Student()

Person::~Person()

Student::~Student()

Person::~Person()

如果在基类中析构函数不加virtual,结果为:

Person::~Person()

Person::~Person()

Student::~Student()

Person::~Person()
*/

可以看出:只有在用基类的指针指向派生类的时候虚函数发挥了动态的作用。

析构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数。如果析构函数不是虚函数,而程序执行时又要通过基类的指针去销毁派生类的动态对象,那么用delete销毁对象时,只调用了基类的析构函数,未调用派生类的析构函数。这样会造成销毁对象不完全,容易造成内存泄露。

16、构造函数声明为explicit
C++中, 一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色。 1 是个构造器 ,2 是个默认且隐含的类型转换操作符。

explicit构造函数是用来防止隐式转换的。

关键字explicit只对一个实参的构造函数有效
需要多个实参的构造函数不能用于执行隐式转换,所以无需将这些构造函数指定为explicit的
只能在类内声明构造函数时使用explicit关键字,在类外部定义时不应重复

17、构造函数为什么不能是虚函数
从C++之父Bjarne的回答我们应该知道C++为什么不支持构造函数是虚函数了,简单讲就是没有意义。虚函数的作用在于通过子类的指针或引用来调用父类的那个成员函数。而构造函数是在创建对象时自己主动调用的,不可能通过子类的指针或者引用去调用。

虚函数相应一个指向vtable虚函数表的指针,但是这个指向vtable的指针事实上是存储在对象的内存空间的。假设构造函数是虚的,就须要通过 vtable来调用,但是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。

18、构造函数和析构函数可不可以调用虚函数
总的来说,构造函数和析构函数调用虚函数并不能达到多态的效果,因为在析构和构造过程中,该对象变为一个基类对象,调用的方法都是基类的方法。

构造函数:在基类的构造过程中,虚函数调用从不会被传递到派生类中。代之的是,派生类对象表现出来的行为好象其本身就是基类型。不规范地说,在基类的构造过程中,虚函数并没有被"构造"。简单的说就是,在子类对象的基类子对象构造期间,调用的虚函数的版本是基类的而不是子类的。
析构函数:一旦一个派生类的析构器运行起来,该对象的派生类数据成员就被假设为是未定义的值,这样以来,C++就把它们当做是不存在一样。一旦进入到基类的析构器中,该对象即变为一个基类对象,C++中各个部分(虚函数,dynamic_cast运算符等等)都这样处理。

19、静态类型和动态类型,静态绑定和动态绑定的介绍
静态类型和动态类型:

对象的静态类型:
对象在声明是采用的类型,在编译期确定;

对象的动态类型:
当前对象所指的类型,在运行期决定,对象的动态类型可以更改,但静态类型无法更改。

静态绑定和动态绑定:

静态绑定:
绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。

动态绑定:
绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期。

class B
{
}
class C : public B
{
}
class D : public B
{
}
D* pD = new D();//pD的静态类型是它声明的类型D*,动态类型也是D*
B* pB = pD;//pB的静态类型是它声明的类型B*,动态类型是pB所指向的对象pD的类型D*
C* pC = new C();
pB = pC;//pB的动态类型是可以更改的,现在它的动态类型是C*


class B
{
        void DoSomething();
        virtual void vfun();
}
class C : public B
{
        void DoSomething();//首先说明一下,这个子类重新定义了父类的no-virtual函数,这是一个不好的设计,会导致名称遮掩;这里只是为了说明动态绑定和静态绑定才这样使用。
        virtual void vfun();
}
class D : public B
{
        void DoSomething();
        virtual void vfun();
}
D* pD = new D();
B* pB = pD;
/*
 * 为了支持c++的多态性,才用了动态绑定和静态绑定。理解他们的区别有助于更好的理解多态性,以及在编程的过程中避免犯错误。
 * 需要理解四个名词:
 * 1、对象的静态类型:对象在声明时采用的类型。是在编译期确定的。
 * 2、对象的动态类型:目前所指对象的类型。是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。
 * 关于对象的静态类型和动态类型,看一个示例:
 *
 *
 *
 * 3、静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。
 * 4、动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期。
 *
 *
 * 让我们看一下,pD->DoSomething()和pB->DoSomething()调用的是同一个函数吗?
 * 不是的,虽然pD和pB都指向同一个对象。因为函数DoSomething是一个no-virtual函数,它是静态绑定的,也就是编译器会在编译期根据对象的静态类型来选择函数。pD的静态类型是D*,那么编译器
在处理pD->DoSomething()的时候会将它指向D::DoSomething()。同理,pB的静态类型是B*,那pB->DoSomething()调用的就是B::DoSomething()。
 *
 * 让我们再来看一下,pD->vfun()和pB->vfun()调用的是同一个函数吗?
 * 是的。因为vfun是一个虚函数,它动态绑定的,也就是说它绑定的是对象的动态类型,pB和pD虽然静态类型不同,但是他们同时指向一个对象,他们的动态类型是相同的,都是D*,所以,他们的调>用的是同一个函数:D::vfun()。
 *
 * 上面都是针对对象指针的情况,对于引用(reference)的情况同样适用。
 *
 * 指针和引用的动态类型和静态类型可能会不一致,但是对象的动态类型和静态类型是一致的。
 * D D;
 * D.DoSomething()和D.vfun()永远调用的都是D::DoSomething()和D::vfun()。
 *
 * 至于那些事动态绑定,那些事静态绑定,有篇文章总结的非常好:
 * 我总结了一句话:只有虚函数才使用的是动态绑定,其他的全部是静态绑定。目前我还没有发现不适用这句话的,如果有错误,希望你可以指出来。
 *
 * 特别需要注意的地方
 * 当缺省参数和虚函数一起出现的时候情况有点复杂,极易出错。我们知道,虚函数是动态绑定的,但是为了执行效率,缺省参数是静态绑定的。
 *
 * 有上面的分析可知pD->vfun()和pB->vfun()调用都是函数D::vfun(),但是他们的缺省参数是多少?
 * 分析一下,缺省参数是静态绑定的,pD->vfun()时,pD的静态类型是D*,所以它的缺省参数应该是20;同理,pB->vfun()的缺省参数应该是10。编写代码验证了一下,正确。
 * 对于这个特性,估计没有人会喜欢。所以,永远记住:
 * “绝不重新定义继承而来的缺省参数(Never redefine function’s inherited default parameters value.)”
 *
 * 关于c++语言
 * 目前我基本上都是在c++的子集“面向对象编程”下工作,对于更复杂的知识了解的还不是很多。即便如此,到目前为止编程时需要注意的东西已经很多,而且后面可能还会继续增多,这也许是很多人
反对c++的原因。
 * c++是Google的四大官方语言之一。但是Google近几年确推出了go语言,而且定位是和c/c++相似。考虑这种情况,我认为可能是Google的程序员们深感c++的复杂,所以想开发一种c++的替代语言。>有时间要了解一下go语言,看它在类似c++的问题上时如何取舍的。
 * */


  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章           查看所有文章
加:2021-07-15 23:42:02  更:2021-07-15 23:42:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/5 22:57:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码