美文网首页Andoid 八股文面试
程序员自我修养之C++篇

程序员自我修养之C++篇

作者: 巴菲伟 | 来源:发表于2020-12-16 15:41 被阅读0次

    一、C++基础语法学习

    1.常量指针 : const int *  b :  指针的地址可以修改,但是内容不可以修改

    2.指针常量 :  int * const  b :  指针的内容可以修改,但是地址不可以修改

    3.引用 : int a = 20 ;  int &b = a;

           a. 引用必须初始化 

           b. 引用一旦初始化就不能发生改变

           c. 引用本质是 : 指针常量,地址不可以改变,数据可以改变

     4.std::uint64_t  :  unsigned long long

           std::uint32_t    :    unsigned int

           std::uint16_t    :    unsigned short 

           std::uint8_t.    :      unsigned char

     5. 将字符串转为long、int 、float类型

            String —> 无符号long    :  std::stoull

            Strng —-> 无符号int      :  std::stoui

            Strng —-> 无符号float  :  std::stouf

      6. 转意符

          std::string str1 = "45";

          int myint1 = std::stoi(str1);

          std::cout << "std::stoi(\"" << str1 << "\") is " << myint1 << '\n';

          输出结果 : std::stoi("45") is 45

      7. C++ 数据类型转换

          static_cast : 静态类型转换,明确把隐式转换变为显示转换,便与可读性

          reinterpreter_cast : 重新解释类型,强制类型转换

          dynamic_cast : 用于虚函数基类与派生类之间引用和对象的转换

          const_cast : 去const属性

    8. C++函数模版的机制

          编译器会对函数模版进行两次编译,第一次是对函数模版本身编译,第二次是在运行时候参数替换后编译

    9.智能指针学习

        auto_ptr : 

            缺点:在编译时无法检测出来,再运行时才报错。创建形式是通过拷贝构造函数和复值,函数运行时会出现空指针

        unique_ptr : 

            优点:拷贝构造函数和复值函数都被delete,禁止使用拷贝构造函数和复值函数创建对象

        shared_ptr : 强指针

            优点:可以使用构造函数和复值函数创建对象

        weak_ptr : 弱指针

        拓展知识:

        shared_from_this : 可以返回一个shard_ptr指针,里面是有weak_ptr实现的

        weak_ptr.lock() 可以拿到一个shared_ptr指针

        shared_ptr线程安全性 : 多个线程同时读同一个shared_ptr对象是线程安全的,但是如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁

     10. move变量 : 将左值转换为右值,将A对象的所有权转移到B对象,只是转移,没有内存拷贝,A然后被释放,B获得A所有的能力

    二、C++多线程学习

        C++11多线程支持thread、atomic、mutex、condition_variable、future5个关键库

    thread : 线程类

    atomic :  原子操作

        atomic :

        atomic_flag : 

    mutex : 互斥量

        mutex : 基本Mutex类

            lock()、unlock()、try_lock()

        recursive_mutex : 递归Mutex类,递归上锁

        timed_mutex : 定时Mutex类

              try_lock_for : 等一段时间是否可以获得锁,获得返回为true,没有获得返回为flase

             try_lock_until : 等到某个时间是否可以获得锁,获得返回为true,没有获得返回为flase           

        recursive_timed_mutex  : 定时递归Mutex类

        lock_guard :  当多个线程抢占资源时候,其中一个锁在异常情况下无法释放,通过这种方式,可以释放锁,防止死锁现象的放生

             使用方法:

            mutex mMutex;

            lock_guard sk1(mMutex);

        unique_lock : 它具有lock_guard一切能力,也可以控制解锁和非解锁更加灵活的控制使用范围

            使用方法:

            mutex mMutex;

            unique_lock sk2(mMutex);           

            sk2.lock();

            sk2.unlock();

    condition_variable :  条件变量,可以做到不同线程通信

        condition_variable :  需要搭配unique_lock使用

        condition_variable_any :  可以搭配任何mutex使用,但效率比condition_variable低

        常用方法:  

            wait : 阻塞自己,等待唤醒

            wait_for : 等某一段时间,一直阻塞自己,等待唤醒

            wait_until :  等到某个时间,一直阻塞自己,等待唤醒

            notify_one : 唤醒某一个 阻塞线程

            notify_all :   唤醒所有阻塞线程   

    future :   异步操作,可以拿到不同线程的结果      

    Futures类的使用:

    int sum(int a, int b) {

    sleep_for(chrono::seconds(5));

        return a + b;

    }

    future result = async(sum, 123, 456);    

    result.wait();           //等待结果算出

    int sum = result.get();       //得到结果

    Promise类:

    使用方法:

    void work(promise<int> &prom) {

    sleep_for(chrono::seconds(5));

        //设置结果值

        prom.set_value(6666);

    }

    promise<int> pro;

    future result = pro.get_future();

    thread t1(work, ref(pro));

    t1.detach();

    int sum = result.get();

    cout<< "输出结果:" << sum<< endl;

    三、数据结构

        1. vector : 一块连续、单向开口、动态数组(myFirst、myLast、myEnd三个指针)

        2. deque : 一块连续、双向开口、动态数组(核心:中控器)

        3. list :  一块非连续、非顺序的双向链表

        4.set和multiset : 底层都是红黑树作为实现

        4. map和multimap :  底层都是红黑树作为实现

    四、C++面试总结

    1.vector : 因为vector使用数组实现的,因此元素是保存在连续的内存中的,所以通过索引取值的速度非常快

       size的大小:结束的数据-开始的数据

       capacity的大小:申请一块连续的内存大小

    2.resize()、reserve()两个函数对比

      resize : 影响的存储的数据的大小,也有可能影响capacity这个大小,因为size长度比capacity的长度大,这个时候就需要扩容

      reserve:它只影响capacity的大小,不会影响size的大小

      扩容时候会把之前旧数据拷贝到新的数据里面,然后再把老的数据清除掉

    3. list : 在底层使用一个双向的环形链表实现的,所以在任意位置进行增加或者删除速度都比较快,都是指针交换

    4.Map、Multimap、Unordered_map、Unordered_multimap

       Map和Multimap:底层基于红黑树,元素自动有序,且插入、删除效率高,时间复杂度是O(logN)

       Unordered_map和Unordered_multimap : 底层基于哈希表,故元素无序,查找效率高,时间复杂度是O(1)

    5.迭代器和指针的区别

       迭代器(iterator)概念 : 对容器里面的数据进行遍历

     迭代器:对指针里面的功能进行一系列封装的指针,它是一个类模版。

        指针:它是一个存放对象的地址的指针变量。

        常用设计模式:单例模式、外观模式、适配器模式、责任链模式、装饰者模式、模版模式、工厂模式、组合模式、代理模式、观察者模式、策略模式、建造者模式

    6.模版 :template作为关键字

        类模类和函数模版的区别

       类模类:调用时候类时候参数类型必须是显性,指定好类型

       函数模版:调用时候函数时候参数可以显性或隐性,指定好类型或者不指定类型

    7.友元函数,需要用friend来作为定义

        类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

    8.指针和引用的区别

       引用创建时候同时被初始化,引用不能为null,指针可以在任何场景下初始化,指针可以是null

        引用一旦初始化就不能修改,指针随时可以改变

        引用的sizeof取决于变量的大小,指针的sizeof的大小是4个字节

        引用有编译检查,指针无编译检查  

        如果返回动态内存分配的对象或者内存,必须使用指针, 不使用引用(可能引起内存泄露)

    9.虚函数用sizeof来看大小

        虚函数用sizeof大小是4,(32位计算器)虚函数指针的大小是4,指针用sizeof去计算大小都是4

    10.虚函数和纯虚函的区别

        虚函数:它是一个继承,子类不一定需要实现,继承只能实现一个,构造函数不能使用虚函数

        纯虚函数:它是一个接口,子类必须实现,它是在方法后面加个=0,接口可以实现多实现

        什么情况下需要将析构函数定义为虚函数?

        当一个类里面有虚函数时候,继承之后就会调用该方法,这样会导致内存泄漏,才必须再析构函数加virtual这个关键字

    11.struct和class的区别    

        class默认访问是private,而struct默认访问是public

        实例化的class存在堆内存中,而实例化的struct存在栈中,它不用执行析构函数,执行效率更高

        class可以继承,而struct不能继承

    12.什么是左值、什么是右值

          左值 : 变量是左值,可以放在左边和右边

          右值 :  常量是右值,只能放在右边       

    13. include的""和<>区别

        #include“”优先从自己定义的源文件中查找,找不到之后才去标准库文件中查找。

        #include<>优先从引入的标准库文件中查找。<>里面一般都放标准库.h。

    14.inline内联函数的作用以及缺点

           作用:代码过短但被经常调用,需要使用内联函数,这样可以大大提高运行效率

           缺点:方法不能过长,也不能有循环语句在里面

            不使用inline内联函数:

            int add(int a, int b) {

                return a + b;     ------------>  (内联函数)

            }

            使用inline函数: inline void add(int a , int b)

    15. 智能指针

        智能指针有:auto_ptr(自动)、shared_ptr(共享)、weak_ptr、unique_ptr(独占)

        auto_ptr : 

        shared_ptr : 共享指针(强指针)

        weak_ptr :   弱指针

        unique_ptr : 独占指针

         作用:申请的空间在函数结束时忘记释放,类会自动调用析构函数,防止造成内存泄漏

    shared_ptr的原理:

    初始化以及增加情况

         1.第一次创建shared_ptr时候,对象计数和引用计数设置为1

         2.当shared_ptr赋值给新的shared_ptr的时候,对象计数+1,引用计数不变

         3.当shared_ptr赋值给新的weak_ptr的时候,对象计数不变,引用计数+1

         4.当weak_ptr获取shared_ptr的时候,对象计数不变,引用计数+1

    减少以及销毁情况

         1.当shared_ptr对象析构时候,对象计数-1,引用计数不变,当对象计数为0时候,SharedPtr释放内存,引用计数-1。

        2.当weak_ptr对象析构时候,对象计数不变,引用计数-1,当引用计数为0时候,*refCount释放内存

    16.const的关键字的作用 

          作用:使内容和变量只有读的功能

          作用范围:

           变量、指针:该函数只具有读的功能,const修饰的变量一定要在构造函数内部初始化

           函数:表明是一个常函数,里面的函数的变量不能修改

    17.深拷贝和浅拷贝的区别

            浅拷贝:只是对指针进行拷贝,最终两个指针指向还是同一块地址

            深拷贝:不只是对指针进行拷贝,里面内容也会一起拷贝,最终两块指针只向不同的地址

    18.static的作用

        作用:数据共享,减小内存,数据存放在静态区,可以保持封装特性,避免命名冲突的问题,不能和const连用,因为static里面没有this这个对象

    19.volatile的作用

        数据不是写到寄存器里面,而是直接写在内存中,表现的是可见性,只是限制编译器优化的作用,表现的是禁止指令重排序

    20.数组指针和指针数组

        数组指针定义:  int (*a)[5] , 它是一个指针,指向一个包含5个int数组的首地址

        指针数组定义:  int * a[5] ,它是一个数组,里面存着5个指针

        区别:指针数组的sizeof是取决于数组的长度,数组指针的sizeof是4个字节

    21.面向对象的特征

         继承、封装、多态       

    22.赋值构造函数 : 又称拷贝构造函数

    使用场景:

        a、一个对象以值传递方式传入函数体    

        b、一个对象以值传递的方式从函数返回

        c、一个对象通过另一个对象进行初始化

    好处:防止没有执行a、b之后,一个对象通过另一个对象初始化,就会导致一个指针指向已经删除的空间

    23.内存的分配

        静态存储区域:负责存储静态成员

        栈存储区域: 负责存储临时变量或者局部变量

        堆存储区域:负责存储new malloc等创建的动态生成的对象

    24.new delete 和 malloc free的区别

        a、malloc/free 是c语言中的库函数,需要头文件支持;而new/delete 是c++中的关键字

        b、malloc需要显式指出所需内存的大小,new是通过编译器内部计算

        c、malloc内存分配成功时,返回的是void*,需要转换成所需要的类型,new内存分配成功时,返回的是对象类型的指针

        d、free只是负责释放内存,delete是调用析构函数再释放内存

    25. const和#define的区别

        const有数据类型,而#define没有数据类型

        const是在编译过程进行检查,而#define是预编译阶段(#代表预编译),会比较安全

    26. 重载、重写、隐藏(重定义)的区别

           重载:在同一个类,方法名一样,参数不同或者返回类型不同

            重写:在父类、子类中,父类的抽象方法在子类实现,子类和父类方法完全一致

            隐藏:

             a、在父类、子类中,父类的方法在子类实现,子类和父类方法完全一致(没有抽象方法)

                b、在父类、子类中,父类的抽象方法在子类实现,子类重载父类

    27. 构造函数为什么不能是虚函数?

           a. 从空间角度分析:vtable指针是存储在对象的内存空间的,构造函数如果是虚函数,对象还没有实例化,也就说内存空间还没有,所以不能构造函数不能为虚函数

            b. 从使用角度分析:虚函数可以继承,如果子类继承父类的构造方法,就不能正确识别对象类型从而不能正确调用析构函数

    28.堆内存和栈内存的区别

        a.栈内存由操作系统分配,堆内存由程序员自己分配。

        b.栈内存空间大小是固定的,堆的大小受限于系统中有效地虚拟内存。

        c.栈的空间由系统决定合适释放,堆内存需要自己决定何时释放。

        d.堆的使用容易产生碎片,但是使用方便。

    29. 内存对齐

        #pragma  pack(n) , 默认为#pragma  pack(4)个字节对齐 

    30. explicit 的关键字

         用来防止由构造函数定义的隐式转换

    31.C++中拷贝构造函数的参数为何一定要用引用类型

        首先可以减少一次内存拷贝,其次就是防止创建无限递归下去    

    32.数组和指针的区别

        a. 数组的sizeof的大小取决于里面变量多少,指针sizeof大小为4

        b. 数组不能操作++或者--,指针可以执行++或者--

        c. 数组不能随意改地址,指针随意可以改地址

    33.set和map的实现以及区别

       实现:

        set和map都是基于红黑树实现的

        区别:

        a. map中的元素是key-value形式,set只有key的数值

        b. map支持下标操做,set不支持下标操做

    so存放位置:

    naviengine/build/intermediates/cmake/release/

     addr2line存放位置:

    /Users/名字/Library/Android/sdk/ndk/16.1.4479499/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line -e (so存放的位置) 000b0eae

    玄马教育:

    https://space.bilibili.com/477729104/video?tid=0&page=1&keyword=&order=pubdate 

    多线程讲解:

    https://space.bilibili.com/147020887/channel/seriesdetail?sid=1745830

    C++中文学习网址:

    https://zh.cppreference.com/w/cpp

    CMake网址:

    https://www.bookstack.cn/read/CMake-Cookbook/README.md

    C++显示汇编的编程网址:

    https://godbolt.org/

    TutorialsPoint: 高级语言文档教程:

    https://www.tutorialspoint.com/cplusplus/index.htm

    Awesome C++ : 常用框架使用

    https://link.zhihu.com/?target=https%3A//github.com/fffaraz/awesome-cpp

    explicit用来防止由构造函数定义的隐式转换

    相关文章

      网友评论

        本文标题:程序员自我修养之C++篇

        本文链接:https://www.haomeiwen.com/subject/rhsjeqtx.html