八股文查漏补缺

C++知识点

1、引用只是给变量起了别名,属于间接访问

2、引用类型必须一致

int *const p;const int *p1;

p1是一个常指针,指向对象不能变,可以改变值,p的值不能变,指向的对象可以改

顶层表示指针是常量,底层表示对象是常量,

decltype(())是引用,一个()是对象的类型

总结:底层const 、顶层const

3、重载: 函数名相同

​ 参数必须不同(个数或类型或顺序)

​ 返回值类型可以相同也可以不同

4、计算机有数值上溢和下流问题,超过最大值和小于最小值

5、类模板调用如下

``

template <``class` `T>
struct sum {
 ``static` `void` `foo(T op1 , T op2){
  ``cout << op1 <<op2; 
 ``}
};
sum::foo(``1``,``3``);
//调用应该如下: sum<int>:: foo(1,3)

6、纯虚函数除了有virtual 关键字外,还令它等于0,以表为纯虚函数。拥有纯虚函数的类称为 抽象类

7、类B从类A派生(public),则类B可以访问类A中的( )成员? public和protected成员

8、构造函数是在(创建对象)时被执行的。

9、int w=1, x=2, y=3, z=4;条件表达w < x ? w : y < z ? y : z的返回值是1,可以看作为w<x?w:(y<z?y:z) 加个括号应该就好理解了 w<x为真,返回w,即表达式的值为1

10、Base * pb = new Child(1), 首先创建子类对象,初始化为1; func1()不是虚函数,所以pb->func1()执行的是基类的func1函数,i= 10,然后调用func2()函数; 这里的func2是虚函数,要往下派生类寻找,找到后执行派生类中的func2(),此时,i = 12; 最后执行pb->getValue(),结果为12 故选C

11、resize(),设置大小(size);reserve(),设置容量(capacity);

12、多态有静态多态(编译时)和动态多态(运行时),静态多态主要实现了函数的重载和运算符的重载,动态时的多态主要实现了虚函数

13、可以发现全局定义的枚举类型变量,如果不定义初值,系统默认赋值0,与枚举变量中的取值无关。局部定义枚举类型变量,如不定义初值则无法调用。

13、当定义一个类时,系统会提供一个默认构造函数,一个默认析构函数,一个默认拷贝构造函数

14、静态数据成员要在类内声明 ,类外定义和初始化,不属于类

15、《C++ Primer》中提到在以下三种情况下需要使用初始化成员列表:
情况一、需要初始化的数据成员是对象的情况(这里包含了继承情况下,通过显示调用父类的构造函 数对父类数据成员进行初始化);
情况二、需要初始化const修饰的类成员;
情况三、需要初始化引用成员数据;

​ 数组是不能在成员列表里面初始化,数组成员初始化需要使用{},二成员列表不支持{};

16、抽象类不能实例化对象,但是可以定义指针和引用。

17、静态变量放在程序的全局数据区,而不是在堆栈中分配,所以不可能导致堆栈溢出

18、二叉树的迭代实现,递归序 利用压栈和出栈的方法,流程思想记清楚,先序、中序、后序(需要一个收集栈)

19、sizeof(类)计算的是类中存在栈中的变量的大小,而类中的b和*c都是static静态变量,存在全局区中,因此不在计算范围之内,于是只剩下char a,void *p和两个virtual虚函数,a是char类型,占用一个字节,p是指针,在64位系统的指针占用8个字节,而两个虚函数只需要一个虚函数表指针,也是八个字节,加上类中的对齐方式(char a对齐时后面补上7个字节),故答案为24.

20、通常来说联编就是将模块或者函数合并在一起生成可执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址,它是计算机程序彼此关联的过程。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编动态联编。 静态联编是指在编译阶段就将函数实现和函数调用关联起来,因此静态联编也叫早绑定,在编译阶段就必须了解所有的函数或模块执行所需要检测的信息,它对函数的选择是基于指向对象的指针(或者引用)的类型,C语言中,所有的联编都是静态联编,并且任何一种编译器都支持静态联编。 动态联编是指在程序执行的时候才将函数实现和函数调用关联,因此也叫运行时绑定或者晚绑定,动态联编对函数的选择不是基于指针或者引用,而是基于对象类型,不同的对象类型将做出不同的编译结果。C++中一般情况下联编也是静态联编,但是一旦涉及到多态和虚拟函数就必须要使用动态联编了。C++多态有两种形式,动态多态和静态多态;动态多态是指一般的多态,是通过类继承和虚函数机制实现的多态;静态多态是通过模板来实现,因为这种多态实在编译时而非运行时,所以称为静态多态

21、构造函数和析构函数的调用,在父类和子类上,构造是父子关系,析构是子父关系。

22、对象属于某个已知的类,是类的实例,对象之间通信实际上就是通过函数传递信息,封装是把数据和操作结合在一起,继承是对于类的方法的改变和补充,重载是多态性之一。

23、数组初值列表中有一行是空的,这在C语言中是不允许的

24、C++ 非虚的成员函数是静态绑定,只要不涉及类成员空指针调用是没有问题。

25、#import是#include的替代指令,防止重复引用

26、方法重载的定义:同一个类或与他的派生类中,方法名相同,而参数列表不同的方法。其中参数列表不同指的是参数的类型,数量,类型的顺序这三种至少有一种不同。与返回值无关

27、函数取地址时,可以直接使用函数名,也可以在函数名前面加&

28、数组下标范围是0~MAX-1,当循环到i = MAX时,A[i] = A[MAX],此时数组发生了越界

29、i的类型是unsigned char,即i的取值范围是0~255,所以当i = 255时,i + 1 = 255 + 1后会导致i = 0,因此for (i = 0; i <= MAX; i++)会一直循环下去

30、有符号数与无符号数比较,会自动转换为无符号数

31、fork()函数会把它所在语句以后的语句复制到一个子进程里,单独执行。
2.如果printf函数最后没有"\n",则输出缓冲区不会被立即清空,而fork函数会把输出缓冲区里的内 容也都复制到子进程里。

32、指针不论指向哪里,都是指针类型大小

33、free释放的内存不一定直接还给操作系统,可能要到进程结束才释放。 可以直到malloc不能直接申请物理内存,它申请的是虚拟内存

34、out 打开应该是默认隐含 trunc ,都是写操作,in是读操作

35、 CSomething a();// 没有创建对象,这里不是使用默认构造函数,而是定义了一个函数,在C++ Primer393页中有说明。 CSomething b(2);//使用一个参数的构造函数,创建了一个对象。 CSomething c[3];//使用无参构造函数,创建了3个对象。 CSomething &ra=b;//ra引用b,没有创建新对象。 CSomething d=b;//使用拷贝构造函数,创建了一个新的对象d。 CSomething *pA = c;//创建指针,指向对象c,没有构造新对象。 CSomething *p = new CSomething(4);

img

36、BSS段:通常是指用来存放程序中未初始化的全局变量的一块内存区域; 数据段:通常是指用来存放程序中 已初始化 的 全局变量 的一块内存区域,static意味着在数据段中存放变量; 代码段:通常是指用来存放 程序执行代码 的一块内存区域; 堆:存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减,这一块在程序运行前就已经确定了; 栈:栈又称堆栈, 存放程序的 局部变量 (不包括static声明的变量)。除此以外,在函数被调用时,栈用来传递参数和返回值。

37、int型数值赋给char型变量时,只保留其最低8位,高位部分舍弃

38、#pragma comment。将一个注释记录放置到对象文件或可执行文件中。

#pragma pack。用来改变编译器的字节对齐方式。

#pragma code_seg。它能够设置程序中的函数在obj文件中所在的代码段。如果未指定参数,函数将放置在默认代码段.text中
#pragma once。保证所在文件只会被包含一次,它是基于磁盘文件的,而#ifndef则是基于宏的

  1. 二叉树类型的容器进行std::sort和std::find时,都会调用operator < 。线性类型(vector、list)容器进行std::sort算法时,会调用operator <;进行std::find时,会调用operator ==。

  2. 静态成员变量在编译阶段分配内存

  3. (1)只能使用成员函数重载的运算符有:=、()、[]、->、new、delete。 (2)单目运算符最好重载为成员函数。 (3) 对于复合的赋值运算符如+=、-=、*=、/=、&=、!=、~=、%=、>>=、<<=建议重载为成员函数。 (4) 对于其它运算符,建议重载为友元函数。 运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。也就是说,运算符重载是通过定义函数实现的。运算符重载实质上是函数的重载。重载运算符的函数一般格式如下: 函数类型 operator 运算符名称 (形参表列) { 对运算符的重载处理 } 重载为类成员函数时参数个数=原操作数个数-1(后置++、–除外) 重载为友元函数时 参数个数=原操作数个数,且至少应该有一个自定义类型的形参

  4. 静态成员可以作为默认实参

  5. 一位16进制数对应四位二进制数,一个byte等于8bite,等于两位16进制数,然后分大端小端,两者相反

  6. 普通成员函数后面加上 const 修饰,就是常函数。
    常函数中的 this 指针是常指针,不能在常函数中对成员变量进行修改,只能读取;
    如果想要在常函数中对成员变量进行修改,可以在成员变量前加上 mutable 关键字进行修饰;
    常函数可以被普通对象或者常对象调用,但是常对象只能调用常函数,常对象只能读成员。

  7. 64位的指针8字节

  8. 只有给对象分配内存才会调用构造函数

  9. .,:: ,?:,sizeof,typeid,.* 这几个运算符不能被重载

  10. 在C++中友元函数是独立于当前类的外部函数,一个友元函数可以同时定义为两个类的友元函数,友元函数即可以在类的内部,也可以在类的外部定义;在类的外面定义友元函数时不必加关键字friend。

  11. 同一个类的不同对象共享一个静态成员变量和函数,静态成员函数没有this指针。

  12. 内联函数宏定义

  13. 在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数。 虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象。

  14. 当类中含有虚函数的时候,创建该类的对象时,该对象的首地址即为虚函数表的地址,无论对该对象进行怎样的类型转换,该对象都只能访问自己的虚函数表

  15. 运算符重载隐含了this指针,因此双目只有一个参数,单目没有参数

  16. 如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量

  17. 一致性哈希:表示使用相同的hash算法将数据和节点都映射到途中的唤醒哈希空间中。

  18. //类的默认构造和析构函数
    class Empty  
    {  
      public:  
        Empty();                            //缺省构造函数  
        Empty(const Empty &rhs);            //拷贝构造函数  
        ~Empty();                           //析构函数   
        Empty& operator=(const Empty &rhs); //赋值运算符  
        Empty* operator&();                 //取址运算符  
        const Empty* operator&() const;     //取址运算符(const版本)  
    };  
    
    
  19. C++规定构造函数不能是虚函数,而析构函数可以是虚函数

  20. 引用只能一层,不能多层引用。

    不允许使用引用的数组。

    指针是指向一个对象的,引用是别名,不是对象,所以不能定义指向引用的指针

  21. 顶层const:指的是const修饰的变量本身是一个常量,无法修改,指的是指针,就是 * 号的右边

  22. 底层const:指的是const修饰的变量所指向的对象是一个常量,指的是所指变量,就是 * 号的左边

  23. override只能用于重写父类,找不到就会报错,所以一般加上会保险一点。

  24. final关键字表示结束某个函数的改变,也就是说,这个函数再也不能被重写或者继承。写在函数后面末尾处

  25. 拷贝初始化和直接初始化区别:可以大概通过是否含有=符号,同时,如果是已存在的对象初始化新对象,则会调用拷贝构造函数。是否会新开空间去保存。

  26. 重写指的是在派生类中覆盖基类中的同名函数,重写就是重写函数体要求基类函数必须是虚函数,重写只有函数体不同,传参以及返回必须一致。还要有virtual

  27. 在继承的时候,private成员始终是不可见的,private继承都为private,public继承,均不变,protected继承,都为protected。

  28. 多态:父指针指向子类是可以的,这也常用于类的扩展中

  29. 重写和重载的区别:重写是派生类,重载是基类自身

  30. 重载和重写的实现,

    1. C++在编译阶段改写函数名,区分参数不同的同名参数
    2. 在编译时使用virtual关键字区分具体的类型,根据具体属于基类还是派生类,调用函数。
    3. 存在虚函数的类都有一个虚函数表,指针指向它,在具体指向基类和派生类的时候根据指针指向调用
    4. 纯虚函数必须=0,存在一个及以上的纯虚函数的类可以叫做抽象类。

68.c语言实现重载方法:1、函数指针;2、重载函数可变参数,如open函数;3、gcc的内置函数,程序使用编译函数可以实现重载;

  1. 有了有参数的构造,编译器便不会提供默认的构造函数

  2. 如果只定义析构函数,会自动生成哪些构造函数? 拷贝构造和默认构造,最好定义自己的这两个构造函数

  3. 定义一个空类,实例化的时候会自动生成默认构造和析构函数、拷贝构造,赋值运算

  4. 野指针和悬空指针(野指针:未初始化)

  5. 在有多重继承的情况下,C++的类初始化顺序:创建派生类对象,优先调用基类的构造函数,其次时成员类和成员类构造函数。多个基类构造函数的调用顺序是在某类派生表中的出现顺序而不是初始表顺序。

    综上:父类->成员类对象->自身,析构则与之相反

  6. 子类转化为父类主要使用dynamic_cast<type_id>(expression),这种转换比较安全;父类转化为子类则可以强制转换,但不安全;

  7. 浅拷贝,值相同,一个地址。深拷贝,新空间,不同地区;

  8. 多态:静态多态:重载;动态多态:继承+重写;

  9. 虚析构,将可能被继承的析构函数设置为虚函数,可以防止内存泄漏,在设计模式也写过。构造则不能虚构造

  10. #include<iostream>
    using namespace std;
    //模板定义,实例化可以分为显示和隐示,显示主要指研发人员动手,隐示为编译器决定类型
    template<class T>
        struct TemplateStruct{
            TemplateStruct(){
    		cout<<sizeof(T)<<endl;
        }
    }
    //模板显示实例化
    template struct TemplateStruct<int>;
    
    //模板具体化
    tempalt<> struct TemplateStruct<double>
    {
        TemplateStruct()
        {
            cout<<"--8--"<<endl;
        }
    }
    int main()
    {
        TemplateStruct<int>intStruct;
         TemplateStruct<double>doubleStruct;
             
        TemplataStruct<char>llStruct;//隐示
    }
    //输出为 4 --8-- 1
    

    移动构造函数:将值从一个对象移动到另外一个对象,源对象将丢失自身的内容。移动操作法神大哥时候是当移动值的对象是未命名的对象时。即存在临时变量时,例如返回值或者类转换对象。将这些临时变量的值移动给局部或全局变量。

    //移动构造函数和赋值
    #include<iostream>
    #include<string>
    using namespace std;
    class Example{
        string *ptr;
    public:
        Example(const steing& str):ptr(new string(str)){}
        ~Example(){delete ptr;}
        //移动构造函数,参数x不能是const Pointer&& x;
        //因为要改变x的成员数据值
        Example(Example&& x):ptr(x.ptr){
            x.ptr = nullptr;
        }
        //move assignment
        Example& operate=(Example &&x){
            delete ptr;
            ptr = x.ptr;
            x.ptr = nullptr;
            return *this;
        }
        //access constent
        const string& content() const{return *ptr;}
        //addition
        Example operate+(const Example& rhs){
            return Example(content()+rhs.content());
        }
    }
    
    1. 常函数的作用表示不对类的数据做出任何改变。
    2. 虚继承的定义以及解决问题:将基类指针指向继承类,解决棱形继承的问题(多重继承)
    3. 虚函数:父类指针调用子类成员函数,虚表存储了关于虚函数实例化的函数,方便具体调用。纯虚函数:完全用来继承,是一个抽象。
    4. static不能用于虚函数和纯虚函数,因为后面两个是基于动态的,而static是静态的。

构造函数中不能调用虚函数,语法没问题,但是达不到想要的效果。主要原因在于类型不一致,比如派生类型和基类型,会产生错误。

  1. 抽象类不能实例化,类函数的的调用是在运行时确定称作动态联编

  2. 拷贝构造函数参数传递只能使用引用,其他传值都不行,因为会造成无穷递归。

  3. 类方法和数据权限有public,protected和private三种

  4. 虚析构函数的作用防止内存泄漏,根据指针释放而不是根据数据类型释放,可以释放基类和派生类,而不加的话只能删除基类

  5. 虚基类可以实例化,产生对象调用,纯虚类不行(抽象类)

  6. 仿函数:又称作函数对象,行驶函数功能的类,必须重载operator()运算符,在一定程度下可以更方便的实现某些功能。

  7. 不能声明为虚函数的函数:非成员函数、静态成员函数,内联成员,友元函数(这是因为C++不支持友元函数的继承)

  8. 类模板是类的模板定义,而不是类定义,模板类是类定义,模板可以有层次,类模板可以作为基类派生出模板类

  9. 虚表在编译阶段形成,对象内存空间开辟以后写入虚表地址。

  10. 迭代器是一个类模板,是模板定义,可以作为基类派生出模板类

  11. 增强for循环里面自动推导的是一个元素而不是迭代器,所以不需要解引用

  12. resize和reserve的区别,一个是可以改变容量,一个是只能改变容量

  13. push_back先构造临时对象再拷贝进去,emplace_back则直接在容器的末尾构造对象,省去了拷贝操作

  14. C++11的新特性:语法改进和标准库的扩充

  15. 统一初始化方法,成员变量默认初始化,auto关键字以及智能制造和decltype求表达式类型,空指针以及右值引用。扩充了无序哈希表、正则表达式和lambda表达式

  16. decltype使用的时候是decltype(exp)varname = value;exp表示一个表达式,根据表达式推导varname的类型;多个share_ptr智能指针可以使用同一片内存,采用计数器判断是否还需要内存;

  17. 右值引用:int &&a =10;这个还是可以修改

  18. 只有构造函数能够使用函数初始化列表,效率更高,不会产生临时拷贝,引用则是因为定义的时候必须初始化,常量const也是如此;

  19. 定义和声明的区别:1、声明是告诉编译器变量的名称和类型,不分配内存。2、定义是为了给变量分配内存,可以复赋值。

  20. 左值可以改变,右值不能变,右值引用能变。移动构造使用右值引用实现。

  21. unique_ptr采用独占式拥有,同一时间只有一个只能指针指向该对象,share_ptr之前讲过,多个智能指针指向同一个内存,采用计数器方法。weak_ptr是一种给不控制对象生命周期的智能指针,指向一个share_ptr管理的对象。不能通过weak_ptr直接访问对象方法,只能转化为share才行。他的出现时为了解决share的死锁问题。

  22. lambda,可变参数模板新特性

  23. explict显示转换析构函数,主要是避免int和char的问题

  24. free的内存会保留地址在ptmalloc双链表中,下次需要直接调用,避免重复的系统开销。

  25. const和static的区别。

  26. final可以让该函数不能再被继承或者重载,override告诉编译器这是继承的函数,帮你检查。

  27. 顶层和底层const的区别在于const在*的左边还是右边,右边的话则是顶层,修饰的变量是一个常量,值可以改,指向不能改。左边则是变量指向一个常量,值不能更改。

  28. 内存泄漏指的是内存分配后未释放,避免方法可以使用计数法、析构函数设计为虚函数,分配和释放函数成对出现,数组释放使用delete []。

  29. 对象的复用本质是一种设计模型,flyweight享元模式。通过将对象存储到对象池中重复利用。

  30. 零拷贝类似于emplace函数,原地构造,而不需要重新开辟空间再复制。

  31. 在C++中静态和多态是相对于编译和运行确定的,静态即在编译时确定的,动态则在运行时确定。

  32. 虚函数才具有动态绑定

  33. 类实现静态分配和动态分配方法:1、静态分配的话使用private重载new和delete运算符。2、动态分配的话使用protectd构造函数于析构函数。

  34. //写一个比较大小的模板
    #include<iostream>
    template<typename T1,typename T2>
    T1 Max(T1 a,T2 b)
    {
        return a>b?a:b;
    }
    using namespace std;
    int main(){
        cout<<"Max = "<<Max(5.5,'a')<<endl;
        return 0;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Curious*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值