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

一、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 &prom) {

sleep_for(chrono::seconds(5));

    //设置结果值

    prom.set_value(6666);

}

promise 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++篇)