C++基础学习


参考:

  1. https://blog.csdn.net/luanfenlian0992/article/details/118771472#t13
  2. https://zhuanlan.zhihu.com/p/335994370
  3. https://zhuanlan.zhihu.com/p/79883965
  4. https://blog.csdn.net/K346K346/article/details/85018985
  5. https://blog.csdn.net/weixin_45626706/article/details/116497723?ops_request_misc=&request_id=&biz_id=102&utm_term=slam%20%E9%9D%A2%E8%AF%95c++%E9%97%AE%E9%A2%98&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-116497723.nonecase&spm=1018.2226.3001.4187#t39

编译内存相关

编译

编译.cpp .h过程分为四个过程:编译(编译预处理、编译、优化.s),汇编.o,链接。

静态链接
动态链接

变量与内存分区

内存分区

  1. 栈区:
    正常函数体内{}"定义的都是。
    连续内存。

  2. 堆区:
    new出来的。
    类似链表的分段内存。

  3. 全局/静态存区:
    static和全局变量extern.

  4. 常量区:
    常量。

  5. 代码区:
    编译后的二进制文件。

变量类型

  1. 全局变量:
    放在.cpp文件里面的,不再{}内的变量,同样的名称多文件共享。
    多个cpp文件共用或者{}内使用需要extern修饰声明。

  2. 静态全局变量:
    static修饰,具有文件作用域,多个文件同样的名称是不同的变量。

  3. 局部变量:
    {}里面定义的。

  4. 静态局部变量:
    static修饰的{}函数中的变量,具有函数作用域,只初始化一次。

内存对齐

对齐存取快,不报异常好迁移。
有些设备不支持非对齐的存取,不能保证原子操作。

内存泄露

new出来的内存,由指针进行操作,后来没delete指针指向别的了,之前的那块内存地址就找不到了,没法删除啥的。

智能指针

智能指针,自动释放内存,防止内存泄露。

  1. shared_ptr
    多个指针共享一块内存,通过引用计数自动释放。

  2. unique_ptr
    一个指针独占一块内存,不能拷贝赋值。可以移动构造和移动赋值构造。

  3. weak_ptr
    指向 shared_ptr 指向的对象,能够解决由shared_ptr带来的循环引用问题。

include “” 和<>

https://www.iteye.com/blog/kooyee-340846解释:
<>先去系统目录中找头文件,如果没有在到当前目录下找。所以像STL标准的头文件 stdio.h、stdlib.h等用这个方法。

""首先在当前目录下寻找,如果找不到,再到系统目录中寻找。 这个用于include自定义的头文件,让系统优先使用当前目录中定义的。

语言对比

c++ 11

自动类型推导

  1. auto
auto var = val1 + val2; //定义+初始化为左边的值
  1. decltype
decltype(val1 + val2) var1 = 0; //定义+初始化为其他值

lembda 表达式 (匿名函数)

[capture list/*捕获的局部变量列表*/] (parameter list/*形参*/) -> return type/*返回变量类型*/
{
	/*具体函数实现*/
   function body;
};

范围for

for (declaration /*列表中的一个元素*/: expression/*列表、数组*/){
    statement
}

这玩意不和python的for差不多吗,多了个declaration的变量类型常用auto

delete 函数和 default 函数

  1. delete 函数:= delete 表示该函数不能被调用。
  2. default 函数:= default 表示编译器生成默认的函数,例如:生成默认的构造函数。
#include <iostream>
using namespace std;

class A
{
public:
	A() = default; // 表示使用默认的构造函数
	~A() = default;	// 表示使用默认的析构函数
	A(const A &) = delete; // 表示类的对象禁止拷贝构造
	A &operator=(const A &) = delete; // 表示类的对象禁止拷贝赋值
};
int main()
{
	A ex1;
	A ex2 = ex1; // error: use of deleted function 'A::A(const A&)'
	A ex3;
	ex3 = ex1; // error: use of deleted function 'A& A::operator=(const A&)'
	return 0;
}

对比C、python

  • C++:面向对象
  • C:面向过程
    • 没有bool类型,用int代替
  • python:解释执行

面向对象

特性

  1. 封装:将对象的数据和过程绑定在一起则被称为封装。
  2. 继承
  3. 多态:子类重写父类的virtual虚函数实现。

重载、重写、隐藏的区别

  1. 重载:
    就是函数的重载,和面向对象没关系

  2. 隐藏:
    子类的同名函数会隐藏父类的同名函数,如果需要调用父类函数需要.Base::fun()

  3. 重写(覆盖):虚函数,纯虚函数(子类一定要重写)
    父类的函数有virtual修饰,函数名、参数列表、返回值类型需要一致。
    主要应用:父类指针指向new子类对象,实现父类指针调用子类的函数。

关键字和库函数

这玩意太多了还是直接看别人写的吧

  1. explicit:避免编译器进行隐式类型转换
  2. static
    1. 修饰类内的变量函数时候,可以不创建类对象直接使用,不能定义为virtual函数。只能访问static成员。因为静态成员函数没有 this 指针。
    2. 静态数据成员的类型可以是所属类的类型,而普通数据成员的类型只能是该类类型的指针或引用。
  3. const:修饰常量不能更改,只能初始化时候赋值。mutable修饰使得const的某些成员一直保持是可以改变的。
  4. #define:在编译预处理阶段进行文本替换
  5. typedef:定义类型的别名
  6. inline:内联函数。
  7. deletedelete[]
  8. new mallocdeletefree :malloc、free 是库函数,而new、delete 是关键字
    1. new:内存分配成功,返回该对象类型的指针;分配失败,抛出 bac_alloc 异常。申请空间时,无需指定分配空间的大小,编译器会根据类型自行计算;申请空间时,返回的类型是对象的指针类型,无需强制类型转换,是类型安全的操作符;
    2. malloc:成功申请到内存,返回指向该内存的指针;分配失败,返回 NULL 指针。在申请空间时,需要确定所申请空间的大小。申请空间时,返回的是 void* 类型,需要进行强制类型的转换,转换为对象类型的指针。
    3. 对于自定义的类型,new 首先调用 operator new() 函数申请空间(底层通过 malloc 实现),然后调用构造函数进行初始化,最后返回自定义类型的指针;delete 首先调用析构函数,然后调用 operator delete() 释放空间(底层通过 free 实现)。malloc、free 无法进行自定义类型的对象的构造和析构。
    4. new 操作符从自由存储区上为对象动态分配内存,而 malloc 函数从堆上动态分配内存。(自由存储区不等于堆)
  9. malloc 的原理?malloc 的底层实现?
  10. C 和 C++ struct 的区别?为什么有了 class 还保留 struct?
  11. union:联合体,只有一个有效的成员,对联合体的不同成员赋值,将会对覆盖其他成员的值,联合体的大小为其内部所有变量的最大值,按照最大类型的倍数进行分配大小。
  12. class 和 struct 的异同
    1. class(private 继承),struct(public 继承)
    2. class 可以用于定义模板参数,struct 不能用于定义模板参数。
  13. volatile:多线程巴拉巴拉的,类似线程锁。
  14. extern:C 语言编写的函数
  15. sizeof(1==1) 在 C 和 C++ 中分别是什么结果?
  16. memcpy 函数的底层原理?
  17. strcpy 函数有什么缺陷?

14.-17.啥的从来没用到过QAQ

类相关

拷贝构造函数

  • 值传递: 对于内置数据类型的传递,直接赋值拷贝给形参(形参表示为函数内的局部变量)。对于类类型的传递,首先要调用该类的拷贝构造函数来初始化形参
  • 引用传递: 无论是内置数据类型的传递还是类类型,传递引用或指针最终都是传递地址值,不会有拷贝构造函数的调用

需要是引用传递,因为值传递首先需要调用拷贝构造函数创建一个类的对象。禁止套娃。

虚函数 与 纯虚函数

  1. 虚函数必须实现,否则编译器会报错;
  2. 包含纯虚函数virtual fun()=0;的类为抽象类不能实例化对象。
  3. 子类继承抽象类,没有完全重写纯虚函数,也是抽象类。

虚函数实现机制

类的对象存储指向虚函数表的指针实现

  • 虚函数表
    1. 虚函数表存放的内容:类的虚函数的地址。
    2. 虚函数表建立的时间:编译阶段,即程序的编译过程中会将虚函数的地址放在虚函数表中。
    3. 虚表指针保存的位置:虚表指针存放在对象的内存空间中最前面的位置,这是为了保证正确取到虚函数的偏移量。

单继承多继承的虚函数表结构

语言特性

左值引用、右值引用、std::move()

右值引用可以修改右值,借助 std::move()转换左值引用为右值引用

  1. 右值引用
int &&a = 10;
a = 100;
int b = 5;
int &&refb=std::move(b);
  1. std::move()
    https://zhuanlan.zhihu.com/p/335994370总结的右值引用好处
    1. 从性能上讲,左右值引用没有区别,传参使用左右值引用都可以避免拷贝。
    2. 右值引用可以直接指向右值,也可以通过std::move指向左值;而左值引用只能指向左值(const左值引用也能指向右值)。
    3. 作为函数形参时,右值引用更灵活。虽然const左值引用也可以做到左右值都接受,但它无法修改,有一定局限性。

指针

大小:64位计算机占8个字节

  1. 普通指针
  2. 常量指针
    1. 常量指针,const int *myptr = aa可以是变量,只是限制不能使用常量指针修改变量a值。
    2. 指针常量,int const * myptr = amyptr本身不能改变即指向的地址不能改变,a的值可以进行修改。
  3. 函数指针
    1. 函数指针,int (*funptr)(int x, int y);funptr可以指向函数int fun(int x, int y){return x*y;}使用funptr = fun;
    2. 函数指针,函数的返回值为指针int * fun(){int * intptr = new int(10);return intptr;}
  4. 指向对象成员的指针
  5. this指针
  6. 野指针和空悬指针
    1. 野指针,未初始化,指向的地址随机
    2. 空悬指针,指向的内存空间已经释放

nullptr 与 NULL

nullptr 具有数据类型,可以转换为任意类型的指针。
NULL 是0,函数重载调用时,会出现无法匹配的情况。

nullptr

nullptr并非整型类别,甚至也不是指针类型,但是能转换成任意指针类型。nullptr的实际类型是std:nullptr_t。

NULL

总结别人的解释https://zhuanlan.zhihu.com/p/79883965
c语言中

#define NULL ((void*)0)

c++语言中

#define NULL 0

NULL是宏定义,在C++中,NULL不过是0,把它当成空指针只是一个无可奈何了。因为C++中不能将void *类型的指针隐式转换成其他指针类型。

指针与引用

  • 指针所指向的内存空间在程序运行过程中可以改变,而引用所绑定的对象一旦绑定就不能改变。(是否可变)
  • 指针本身在内存中占有内存空间,引用相当于变量的别名,在内存中不占内存空间。(是否占内存)
  • 指针可以为空,但是引用必须绑定对象。(是否可为空)
  • 指针可以有多级int **myptp,但是引用只能一级。(是否能为多级)

强制类型转换

static_cast

int a = 10;
doubel b = static_cast<double>(10);
  1. 可以:整型和浮点型、字符型之间的互相转换。空指针转化成目标类型的空指针。任何类型的表达式转化成 void 类型。
  2. 不能:在不同类型的指针之间互相转换,整型和指针,不同类型的引用之间。
  3. 父类和子类,子类转换为父类安全,父类转换为子类有风险。

dynamic_cast

通过“运行时类型检查”来保证安全性的。

  1. 可以:专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 NULL 指针。

const_cast

去除 const 属性的转换。

  1. 可以:将 const 引用转换为同类型的非 const 引用。将 const 指针转换为同类型的非 const 指针。
  2. 不能:用于去掉变量的常量性。

reinterpret_cast

reinterpret_cast 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个bit复制的操作。

就是随便程序员转换,挂了自己负责。

判断结构体相对

自定义比较函数,重载运算法==啥的。
memcmp是逐个字节比较,然而可能存在内存对齐,对齐时候的补的字节是随机的。

参数值传递、引用传递、指针传递

模板

参考https://blog.csdn.net/K346K346/article/details/85018985

  1. 函数模板、类模板区别:
    • 实例化方式不同:函数模板实例化由编译程序在处理函数调用时自动完成,类模板实例化需要在程序中显式指定。
    • 默认参数template<typename T1=int,typename T2>:类模板在模板参数列表中可以有默认参数。c++11之后类模板和函数都可以有。
    • 特化:函数模板只能全特化;而类模板可以全特化,也可以偏特化。
    • 调用方式不同:函数模板可以隐式调用,也可以显式调用;类模板只能显式调用。

函数模板

template<typename T>
T fun(T x, T y)
{
	return x+t;
}

int x,y;
fun(x,y);//隐式调用
fun<int>(x,y);//显式调用

类模板

template <typename T>
class AAA
{
public:
    //构造函数
    AAA(T a, T b)
    {
        this->a = a;
        this->b = b;
    }
    //运算符重载
    AAA<T> operator+(AAA &c)
    {
        AAA<T> tmp(this->a + c.a, this->b + c.b);
        cout << tmp.a << " " << tmp.b << endl;
        return tmp;
    }
private:
    T a;
    T b;
};

Complex<int> a(10, 20);//显式调用

可变参数模板

template <typename T, typename... Args> // Args 是模板参数包
void fun(T &t, Args&... rest); // 可变参数模板,rest 是函数参数包

template <typename T> 
void fun(T &t);

本质上是递归调用fun( T &t, Args&... rest),直达参数只剩一个调用fun(T &t)结束。

#include <iostream>
using namespace std;

template <typename T>
void print_fun(const T &t)
{
    cout << t << endl; // 最后一个元素
}

template <typename T, typename... Args>
void print_fun(const T &t, const Args &...args)
{
    cout << t << " ";
    print_fun(args...);
}

int main()
{
    print_fun("Hello", "wolrd", "!");
    return 0;
}
/*运行结果:
Hello wolrd !

*/

特化

类模板

template<class T1,class T2>
class Test
{}
//全特化
template<>
class Test<int,int>
{}

//偏特化
template<class T>
class Test<int,T>
{}

函数模板

//普通模板
template<class T1,class T2>
bool Compare(T1 a, T2 b)
{
 return a == b;
}

//函数模板特化
template<>
bool Compare(const char* a, const char* b)
{
 return strcmp(a,b) == 0;
}

泛型编程如何实现

模板
容器
迭代器

STL中了解一下底层实现

设计模式

ennn再说吧

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值