1 2 3 4

c++知识点速刷

语法

指针和引用

指针:存放某个对象的地址
引用:变量的别名,从一而终,不可变,必须初始化

const变量

指针常量(底层const):指针所指的对象不可变
常量指针(顶层const):指针不可变

define和typedef的区别

define:
  1. 只是简单的字符串替换,没有类型检查
  2. 是在编译的预处理阶段起作用
  3. 可以用来防止头文件重复引用
  4. 不分配内存,给出的是立即数
typedef
  1. 有对应的数据类型,需要进行判断
  2. 在编译、运行的时候起作用
  3. 在静态存储区中分配空间

define和inline的区别

define:定义预编译时处理的宏,只是字符串替换,无类型检查,不安全。
inline:直接将函数体插入被调用的地方,减少了压栈,跳转和返回的操作。
内联函数是一种特殊的函数,会进行类型检查。

override和 overload

override:重写,覆盖
overload:通过不同的参数类型,不同的参数个数,不同的参数顺序进行重载。访问权限,返回类型,抛出的异常不进行重载
总结:重写只有一个,重载是将类似的函数用同一个名字,仅通过参数区分
多态是为了避免在父类中进行过多的重载

new和malloc

  1. new分配失败抛出异常,malloc返回nullptr
  2. new无需指定内存块的大小,malloc需要显示指出所需内存的尺寸
  3. new和delete可以重载,malloc和free不能重载
  4. new会调用对象的构造析构函数,malloc不会
  5. new是运算符,malloc是库函数

constexpr和const

constexpr:常量表达式
const:只读
constexpr的好处:确保某些数据无法被修改;部分场景编译器可以对constexpr做优化;比宏更安全,开销更少。

volatile

作用:要求编译器读取数据时,每次都从内存中重新装载内容,而不是从寄存器中拷贝

extern

定义:声明在函数或者文件外定义的全局变量

static

作用:实现多个对象之间数据的共享和隐藏;默认初始化为0

前置++与后置++

前置++返回左值(引用),后置++返回右值(临时对象)

std::atomic

作用:为了线程安全,使得原本线程不安全的语句变成“原子”语句,不再可分,从而实现线程安全。

访问权限

pubic:公有
protected:保护
private:私有
在类内(定义类的代码内),三种都可以访问。
在类外(定义类的代码外),只能访问public成员。
无论何种继承,派生类都不能访问私有成员。
对于公有继承,“派生类对象”只能访问基类中的公有成员。对于私有和保护继承,“派生类对象”无法访问基类的所有成员。
1. 继承
功能:在现有类的基础上,定义一个新的类,使得可以在无需重新编写的情况下,进行功能拓展。
  • 实现继承:使用基类的属性和方法,无需额外编码
  • 接口继承:仅使用属性和方法的名称,具体实现由子类提供
  • 可视继承:指子窗体类,使用了基窗体类的外观和实现。
2. 封装
功能:将数据和代码捆绑在一起,避免外界干扰和不确定性访问
3. 多态
功能:向不同对象,发送同一消息,不同对象会产生不同的行为
重载实现编译时多态,虚函数实现 运行时多态
总结:允许将子类类型的指针赋值给父类类型的指针。
多态的实现:
  1. override(重写,覆盖):子类重新定义父类的虚函数。
  2. override(重载):允许存在多个同名函数,通过不同的参数列表区分。

虚函数(virtual)

功能:当基类希望派生类自定义函数时,将该函数声明为虚函数。
  1. 虚函数是动态绑定的:使用虚函数的指针和引用可以找到实际类的对应函数
  2. 多态:前提条件(1)调用函数的对象必须是指针或者引用(2)被调用的函数必须是虚函数,且完成了重写
  3. 动态绑定绑定的是动态类型:虚函数对应的函数或属性依赖于对象的动态类型,发生在运行期。
  4. 构造函数不能是虚函数:在构造函数中调用虚函数,会执行父类的对应函数,因为自身还没有构造好,无法多态。
  5. 析构函数可以是虚函数,且在复杂类中,通常必须是虚函数。析构函数也可以是纯虚函数。
  6. 虚函数的工作方式:虚函数依赖虚函数表工作,表中保存虚函数地址,当用基类指针指向派生类时,虚表指针指向派生类的虚函数表
  7. 纯虚函数:有纯虚函数的类是抽象类,无法实例化。
  8. inline(编译时展开), static(静态), constructor(子类未构造) 三种函数不能为虚函数
  9. 派生类的虚函数重写定义必须与父类完全一致。
为什么需要虚继承
虚继承的本质是,让一个类共享他的基类。
  1. 解决多继承(从多个直接基类中产生派生类)的命名冲突和冗余数据问题。(实际中最好不要用多继承)
  2. 解决菱形继承,典型案例:iostream从istream和ostream直接继承而来,而istream和ostream各自继承自同一个类baseios。
空类
空类size为1,为了有地址,不同的空类地址不同
如果派生类继承的第一个是基类,且基类定义了虚函数表,则派生类共享该表首地址占用的存储单元。
抽象类与接口的实现
类中有任意一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数通过在申明中使用” = 0“来指定。
设计抽象类(也称ABC)的目的,是为了创造一个不能实例化的基类,作为接口使用。

智能指针

shared_ptr
1.实现机制:在拷贝构造时使用同一份引用计数
  1. 模板指针:指向实际的对象
  2. 引用计数:使用new,存放在堆中
  3. 重载operator* 和operator->:实现指针的效果
  4. 重载copy constructor:引用计数+1
  5. 重载operator=:对右边的shared_ptr 调用析构函数,对左边的调用拷贝构造
  6. 重载析构函数:使应勇次数减一,当引用为0时,delete
2. 线程安全问题
同一个shared_ptr被多个线程“读”是安全的。
同一个shared_ptr被多个线程“写”是不安全的。
共享引用计数的不同的shared_ptr被多个线程“写”是安全的。
举例:现有三个shared_ptr,分别为s, a, b。其中s是多线程共享的,s指向对象A,a在线程1中,为空,b在线程2中,指向对象B。智能指针赋值有两步操作,1:指针赋值,2:引用计数加一。线程1先执行a=s,在完成指针复制后,a指向对象A,线程2抢占获得了运行资源,执行s=b,指针复制后,s指向对象B,b引用计数加一,对象A引用计数减一,归零,对象A被delete。此时,智能指针a指向了被销毁的对象,发生错误。
2. unique_ptr
unique_ptr唯一拥有其所指的对象:同一时刻,只能有一个unique_ptr指向给定对象,离开作用域时,若其指向对象,则delete该对象
unique_ptr需要绑定new操作符分配的堆中的对象
不支持拷贝和赋值,但可以通过release和reset移交指针的所有权
3. weak_ptr
为了配合shared_ptr而引入的指针,可能查看对象,但不增加引用计数,和shared_ptr指向相同的内存。
功能:作为访问内存的工具,且不影响对象的生命周期
C++强制类型转换
关键字:static_cast, dynamic_cast, reinterpret_cast, cosnt_cast
1. static_cast
没有运行时类型检查来保证转换的安全性
进行上行转换(把派生类的指针,引用 转换为基类)是安全的。
进行下行转换,因为派生类可能有不属于基类的变量,所以是不安全的。
总结:静态转换,需要程序员自己确定转换的安全性
2.dynamic_cast
具有类型检查(信息在虚函数中),更安全。转换后必须是类的指针、引用或者void*,基类要有虚函数。
dynamic本身只能用于存在虚函数的父子关系的强制类型转换;对于指针,转换失败则返回nullptr,对于引用,转换失败会抛出异常。
总结:当父类需要使用子类的函数,且父类自身的该函数不能设置为虚函数时(如果可以设置为虚函数,编译器会自动动态选择合适的版本)。使用时要加倍小心,尽量使用“定义虚函数”的方法。
3.reinterpret_cast
可以将整形转换为指针,也可以把指针转换为数组;可以在指针和引用间随意转换,平台移植性价比差。
4.const_cast
去掉类型的const或volatile属性。
总结:少用强制类型转换
 

C++内存模型

字符串操作函数
char *strcpy(char *strDest, const char *strSrc);
//把src字符串拷贝到dest字符串上。
int strlen(const char *str);
//计算给定字符串的长度
char *strcat(char *dest, cosnt char *src);
//把src接到dest字符串的后面。
int strcmp(const char * s1, const char * s2);
//比较大小。s1 < s2 返回负数 = 返回0, >  返回正数
内存泄露
定义:程序未能释放掉不再使用的内存的情况,称为内存泄漏。可以使用Valgrind,mtrace进行内存泄漏检查
分类:(1)堆内存泄露:开辟的堆内存没有释放。(2)系统资源泄露:程序使用系统分配的资源(Bitmap, handle, SOCKET等),没有使用相应的函数释放,导致系统资源的浪费。(3)没有将基类的析构函数定义为虚函数,导致子类没有正常释放
常见情况:指针改变指向,未释放动态分配的内存
措施:将内存的分配封装在类中,构造分配,析构释放;使用智能指针
 
地址空间分布
  1. 命令行参数和环境变量:从命令行执行程序时,程序员指定的参数
  2. 栈区:存储局部变量、函数参数值。从高地址向低地址增长的连续空间
  3. 文件映射区:绑定动态库等
  4. 堆区:动态申请的内存。从低地址向高地址增长
  5. BSS段:存储未初始化的全局变量和静态变量
  6. 数据段:存储已初始化的全局变量和静态变量
  7. 代码段:存放程序代码的内存区,只读,头部包含一些只读的常数变量。
静态存储区:编译时分配,生存周期为整个运行期间,如全局变量,static变量。
栈:执行函数时,函数的局部变量都可以在栈上创建,生存周期为函数的运行时间,自动释放。
堆:程序员自己申请(new, malloc)和释放(free,delete),生存周期由自己决定。

 C++11新特性

类型推导

1.auto

作用:让编译器在编译期自动推导出变量的类型
  1. 必须初始化
  2. auto不能定义数组
  3. 不能推导出模板参数
  4. 在不声明为引用或指针时,会忽略等号右边的引用类型,和const, volatile类型。
  5. 声明为引用或指针时,会保留。

2. decltype

decltype用于推导表达式类型。
decltype会保留引用和cv属性(const,volatile)
对于decltype(exp)有:
  1. exp是表达式,decltype(exp)和exp类型相同
  2. exp是函数调用,decltype(exp)和函数返回值类型相同
  3. 其他情况,若exp是左值,decltype(exp)是exp类型的左值引用
例子
template <typename T, typename U>
auto add(T t, U u) -> decltype(t + u) {
    return t + u;
}

右值引用

定义:右值引用就是必须绑定到右值的引用,通过&&来获得右值引用。
性质:只能绑定到一个将要销毁的对象,于是,我们可以自由地将一个右值引用的资源绑定到另一个对象中。
常规的引用,我们可以称之为左值引用。

1.左值右值

左值:有地址,可以放在等号左边
右值:没有地址,不可以放在等号左边(字面量,临时量)
++i, --i 是左值,i++, i-- 是右值。

2.右值引用

右值有两个特性:所引用的对象将要被销毁;该对象没有其他用户
这意味着:使用右值引用的代码可以自由地接管所引用的对象的资源
右值引用本身是一个左值:接管资源后,资源就有了地址和用户,因此,右值变成了左值。

3.std::move

虽然不能把右值引用绑定到左值上,但可以使用std::move把一个左值转化为对应的右值引用类型。
浅拷贝:拷贝指针
深拷贝:重新开辟内存,并复制内容

nullptr

nullptr:特殊类型的字面量。
用来替代NULL和0。NULL在有的编译器中定义为((void*)0),有的定义为0。因此,NULL具有二义性,在下面情况会发生问题。
void test(char*);
void test(int);
使用test(NULL)时,编译器会不知道使用哪一个重载。

范围for循环

for (auto c : string) {
    cout << c;
}
可以简化遍历。

列表初始化

double d = 3.124564564654
int a = {d};
int a = d;
使用花括号来进行初始化称为列表初始化。
对于内置类型,使用列表初始化时,如果存在丢失信息的风险,编译器会报错。

lambda表达式

表示一个可调用的代码单元,可以理解为没有命名的内联函数,用于一次性的场景。

1.语法

[capture list] (parameter list) ->return type {functon body}
我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体。

2.变量捕获

  1. []不捕获
  2. [&]引用捕获
  3. [=]拷贝捕获
  4. [=, &foo]引用捕获foo,拷贝捕获其他
  5. [bar]拷贝捕获bar
  6. [this]捕获所在类的this指针

3.lambda使用algorithm库

std::sort(arr, arr+6, [](int &a, int &b){return a > b});
 
 
 
posted @   木木木999  阅读(90)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示