C++服务端面试准备(1)C++相关

声明:本文内容纯属博主自己查找和归纳的个人所需的知识点,仅作参考,如有错误,博主强烈希望您指出。如果您是某个知识点的原创博主,如有需要,可联系本人加上链接。本文内容会根据博主所需进行更新,希望大家多多关照。

面向对象

       把数据及对数据的操作方法放在一起,作为一个相互依存的整体

       OOP——面向对象的编程,OOD——面向对象的设计,OOA——面向对象的分析

三大特征:

  1. 封装
           将对象的属性和方法封装到一个独立单元中,并且隐藏对象的属性和方法,仅对外提供公共访问方式,将变化隔离,便于使用。

  2. 继承
           继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法。继承是多态的前提。

  3. 多态
           多态是面向对象的最后一个主要特征,它本身主要分为两个方面:

  • 方法的多态性:重载与覆写
           重载:同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同。
           重写:子类中的方法与父类中的某个方法的名称和参数完全相同,子类对象调用这个方法时,将调用子类中方法

  • 对象的多态:父子类对象的转换。
           向上转型:子类对象变为父类对象,格式:父类 父类对象 = 子类实例,自动;
           向下转型:父类对象变为子类对象,格式:子类 子类对象 = (子类)父类实例,强制。

     多态存在的三个必要条件:继承、重写、父类引用指向子类对象

c++类里面编译器默认生成的函数

1.构造函数
2.拷贝构造函数
3.赋值构造函数
4.析构函数

voliate关键字的作用

       使用voliate声明变量值的时候,系统总是重新从它所在的内存读取数据;用voliate声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错.
       使用场景:多任务环境下各任务间共享的标志应该加voliate;存储器映射的硬件寄存器通常也要加voliate,因为每次对它的对写都可能有不同意义;

const的用法

       const修饰的内容不可改变,C++有5种用法:

  1. 定义常量

  2. 指针常量和常量指针:

  • 指针常量——指针类型的常量int *const p
           本质上一个常量,指针用来说明常量的类型,表示该常量是一个指针类型的常量。在指针常量中,指针自身的值是一个常量,不可改变,始终指向同一个地址。在定义的同时必须初始化;

  • 常量指针——指向“常量”的指针const int *pint const *p
           常量指针本质上是一个指针,常量表示指针指向的内容,说明该指针指向一个“常量”。在常量指针中,指针指向的内容是不可改变的

  1. const修饰函数传入参数

  2. 修饰函数返回值

  3. const修饰成员函数(c++特性)
           const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数;
           const对象的成员是不能修改的,但是可以用另外的指针指向成员去修改;
           const成员函数不可以修改对象的数据,不管对象是否具有const性质。编译时以是否修改成员数据为依据进行检查。

const和宏的区别

  1. const是有数据类型的常量,而宏常量没有,编译器可以对前者进行静态类型安全检查,对后者仅是字符替换,没有类型安全检查

  2. 有些编译器可以对const常量进行调试, 不能对宏调试。

  3. 宏能作为卫哨来防止文件的重复包含,但const不可以

static的作用

  1. 隐藏。static修饰的变量只在当前文件生效,故使用static在不同的文件中定义同名函数和同名变量,不必担心命名冲突。

  2. 保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。

  3. static的第三个作用是默认初始化为0

new和malloc的区别

       new和delete是c++的运算符,malloc和free是C语言的标准库函数

特征 new/delete malloc/free
分配内存的位置 自由存储区
内存分配失败返回值 完整类型指针 void*
内存分配失败返回值 默认抛出异常 返回NULL
分配内存的大小 由编译器根据类型计算得出 必须显式指定字节数
处理数组 有处理数组的new版本new[] 需要用户计算数组的大小后进行内存分配
已分配内存的扩充 无法直观地处理 使用realloc简单完成
是否相互调用 可以,看具体的operator new/delete实现 不可调用new
分配内存时内存不足 客户能够指定处理函数或重新制定分配器 无法通过用户代码进行处理
函数重载 允许 不允许
构造函数与析构函数 调用 不调用

C++存储机制

  1. 栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量。

  2. 堆区(heap): 存放malloc申请的内存,使用free释放

  3. 全局区(静态区):存放全局变量和静态变量,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放。

  4. 常量区 :常量字符串就是放在这里的。 程序结束后由系统释放。

  5. 自由存储区:存放new申请的内容,使用delete

为什么会出现段错误

  1. 访问不存在的内存地址

  2. 访问系统保护的内存地址

  3. 访问只读的内存地址

  4. 栈溢出

引用和指针的区别

  1. 引用是变量的别名,一定有本体,指针是是存放地址的一个变量

  2. 引用声明时就必须初始化,指针可以暂时不初始化

  3. 指针的使用需要注意指向的内存,防止出现野指针的情况,引用则不需要

你所了解的c++常见的标准库

       <iostream>、<fstream>、各种容器、<algorithm>、<ctime>、<memory>(智能指针)、<cstdio> 、<functional>(lambda表达式)、<complex> 、<cmath>、<cstring>(free,malloc需要包含<stdlib.h>)、<iterator>等等

三种智能指针

       注意,智能指针只是指针本身自动释放,解决野指针的问题,但其对象一样要手动释放

  1. unique_ptr:
           一个 unique_ptr “拥有” 它所指向的对象。某个时刻只能有一个 unique_ptr 指向一个给定对象。当 unique_ptr 被销毁时,它所指向的对象也被销毁。不能拷贝和赋值,但可以用std::move、release、reset转移,或者用函数返回局部的unique_ptr

  2. shared_ptr
           最终的实现是两个指针成员:一个指向数据成员,一个指向计数器成员,计数器维护的是一个指针,指向的实际内存在堆上,可以用make_shared<>()创建和初始化shared_ptr
    释放方法:
           如果该shared_ptr是唯一指向其对象的shared_ptr,则直接可以<shared_ptr> = nullptr或者<shared_ptr>.reset()释放对象

  3. weak_ptr
           weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放。
    作用:
           可判断shared_ptr的对象是否存在(使用 lock方法),从而避免访问一个不存在的对象的情况。
           解决shared_ptr循环引用问

虚函数原理

       虚函数是用来实现动态绑定的。

       C++中虚函数使用虚函数表和虚函数表指针实现,虚函数表是一个类的虚函数的地址表,用于索引类本身以及父类的虚函数的地址,假如子类重写了父类的虚函数,则在虚函数表中会把对应的虚函数地址替换为子类的函数的地址;虚函数表指针存在于每个对象中,它指向对象所在类的虚函数表的地址;在多继承环境下,会存在多个虚函数表指针,分别指向对应不同基类的虚函数表。

       虚函数表是每个(有虚函数的)类对应一个。虚函数表指针是每个对象对应一个。

       如果一个函数不是虚函数,那么对它的调用在编译阶段就会确定。而虚函数要运行时才能确定。

为什么需要虚析构函数

       在存在类继承并且析构函数中需要析构某些资源时,析构函数需要是虚函数。否则若使用父类指针指向子类对象,在delete时只会调用父类的析构函数,而不能调用子类的析构函数,造成内存泄露。

如何解决类之间的相互依赖问题

  1. 写两个头文件A.h和B.h分别用于声明类A和B;

  2. 写两个.cpp文件分别用于定义类A和B,定义B的.cpp文件里包含A.h;

  3. 在A的头文件中导入B的头文件,在main文件包含A.h即可;

  4. 在B的头文件中不导入A的头文件,前置声明类A,并且,在B中使用A的时候要用指针的形式。

单例设计模式

保证一个类仅可以有一个实例化对象,并且提供一个可以访问它的全局接口。

实现单例模式必须注意一下几点:

  • 单例类只能由一个实例化对象。
  • 单例类必须自己提供一个实例化对象。
  • 单例类必须提供一个可以访问唯一实例化对象的接口

2种方法:

  • 双重判断+上锁
  • 使用std::call_once函数
    std::call_once()函数:第二个参数为函数名,保证函数只调用一次,比互斥量消耗小

注意:可以创建内部类进行释放对象内存,不能用析构函数,因为会出现一直调用析构函数的死循环

双重判断+上锁:

std::mutex resource_mutex;

class MyCAS
{
private:
    MyCAS(){}//构造函数私有化,不能直接创建对象
    static MyCAS *m_instance;
    
public:
    static MyCAS *GetInstance()
    {
        if(m_instance == NULL)//双重判断+上锁,防止多线程多次new对象
        {
            std::unique_lock<std::mutex> mymutex(resource_mutex);
            if(m_instance == NULL)
            {
                m_instance = new MyCAS();
                static CGarhuishou cl;
            }
        }       
        return m_instance;
    }
    
    //此类用于自动删除内存,不能用析构函数,因为在delete了外部指针,会调用析构函数delete m_instance,
//而m_instance指向对象为MyCAS,然后又会再次调用析构函数,变成死循环
    class CGarhuishou
    {
    public:
        ~CGarhuishou()
        {
            if(MyCAS::m_instance)
            {
                delete MyCAS::m_instance;
                MyCAS::m_instance = NULL;
            }
        }
    };
};

MyCAS *MyCAS::m_instance = NULL;
MyCAS *p_a = MyCAS::GetInstance();//创建线程前先初始化单例对象

使用std::call_once函数:

std::once_flag g_flag;
std::mutex resource_mutex;

class MyCAS
{
private:
    MyCAS(){}
    static MyCAS *m_instance;
    
public:
    static void CreateInstance()
    {
        m_instance = new MyCAS();
        static CGarhuishou cl;
    }
    static MyCAS *GetInstance()
    {
        //第一个线程进来call_once执行函数后,第二个线程才能进来判断是否执行
        std::call_once(g_flag, CreateInstance);//g_flag看成执行了函数的标记
        return m_instance;
    }
    
    class CGarhuishou
    {
    public:
        ~CGarhuishou()
        {
            if(MyCAS::m_instance)
            {
                delete MyCAS::m_instance;
                MyCAS::m_instance = NULL;
            }
        }
    };
};

工厂设计模式

  • 简单工厂模式:由一个工厂判断类型然后创建不同的对象
  • 工厂方法模式:多个工厂创建相应的对象
  • 抽象工厂模式:多个工厂创建相应的对象组合

i++和++i的区别

  • 注意不要只看问题表面,可以拓展
  1. i++是返回原来的值再+1,++i是+1再返回值

  2. i++不能作为左值,++i可以作为左值(左值:可以用&获取内存地址),因为重写前缀++的函数内容是+1操作,返回值为引用形式,而后缀++的函数内容是把原来的值赋给一个临时的变量,然后原来的值+1,返回临时变量

  3. 如果i是迭代器或者其他自定义的类,根据第二点的描述,++i的效率明显高于i++

引用和指针的区别

  1. 引用是变量的别名,一定有本体,指针是是存放地址的一个变量

  2. 引用声明时就必须初始化,指针可以暂时不初始化

  3. 指针的使用需要时刻注意指向的内存,防止出现野指针的情况,引用则不需要

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容