C++知识速记

面向对象

在C++中,创建对象的两种方式有本质的不同,分别代表了在栈上和在堆上创建对象

1. SpeechManager* sm = new SpeechManager();

堆上创建对象(动态分配内存):

  • 使用new运算符,意味着SpeechManager对象是在堆上分配的
  • 返回类型是SpeechManager*,即指向SpeechManager对象的指针
  • 需要显式地释放内存,以避免内存泄漏,通常使用delete sm;

2. SpeechManager sm;

栈上创建对象(自动分配内存):

  • sm是一个SpeechManager类型的对象,不是指针
  • 不需要手动释放内存,自动管理内存减少了内存泄漏的风险
  • 对象的生命周期受限于其作用域,当该作用域结束时(比如函数返回时),对象会被自动销毁,内存会被自动释放

全局变量\局部变量\静态局部变量

全局变量:

  • 作⽤域:整个程序
  • ⽣命周期:与程序的⽣命周期相同
  • 使用场景:当多个函数需要共享相同的数据时,可以使⽤全局变量

局部变量:

  • 作⽤域:限定在定义它的块(⼤括号内)
  • ⽣命周期:在块结束时销毁
  • 使用场景:当变量只在某个特定作⽤域内有效,并且不需要其他作⽤域访问时,可以使⽤局部变量

静态局部变量:

  • 作⽤域:限定在定义它的函数内
  • ⽣命周期:与程序的⽣命周期相同,但只能在定义它的函数内部访问
  • 关键字:使⽤static关键字修饰
  • 初始化:仅在第⼀次调⽤函数时初始化,之后保持其值
  • 使用场景:当希望在函数调⽤之间保留变量的值(即“静态”),并且不希望其他函数访问这个变量时(即“局部”),可以使⽤静态局部变量

局部变量与静态局部变量的区别:

局部变量在每次函数调用时都会重新创建,并且初始化为指定的初值。但是,静态局部变量只会在第一次调用函数时初始化一次,之后它的值会保留下来,直到程序结束。

静态局部变量: 只初始化一次,之后保持其值,因此输出为 1 2

#include <iostream>
using namespace std;

void test01() {
    static int count = 0;  // 静态局部变量
    count++;
    cout << "Count: " << count << endl;
}

int main() {
    test01();
    test01();

    return 0;
}

局部变量: 每次调用函数都要初始化,因此输出为 1 1

#include <iostream>
using namespace std;

void test01() {
    int count = 0;  // 局部变量
    count++;
    cout << count << endl;
}

int main() {
    test01();
    test01();

    return 0;
}

&的特性

在C++中,取地址符&和引用符&看似相同,但它们在不同的上下文中有不同的含义

1.取地址符:当你在一个变量前面使用&时,它表示获取该变量的内存地址,例如:

int a = 10;
int* ptr = &a; // ptr 现在指向 a 的地址

2.引用符:当你在一个变量前面使用&时,它表示获取该变量的内存地址,例如:

int b = 20;
int &ref = b; // ref 是 b 的引用,现在 ref 和 b 指向同一个内存

或者:

void increment2(int &value) { 
    value += 1;
}

引用本质上是一个别名,它指向原始对象而不是创建该对象的副本

当你将一个对象传递给函数或在循环中使用时,如果不使用引用,通常会发生复制,即创建一个新的实例。对于大对象(如复杂的数据结构、类实例等),这种复制过程可能会消耗较多的内存和处理时间

通过使用引用,你仅仅是在操作原始对象,而不是其副本,这意味着:

  • 没有额外的内存分配(除了引用本身占用的少量内存)
  • 避免了复制构造函数的调用,提高了程序的性能

函数指针

函数指针是指向函数的指针变量,它可以存储函数的地址并允许通过该指针调用函数

返回类型 (*指针变量名)(参数类型1, 参数类型2, ...);

假设有一个简单的函数:

int add(int a, int b) {
    return a + b;
}

我们可以定义一个指向 add 函数的函数指针:int (*funcPtr)(int, int) = add;

然后可以通过这个指针调用 add 函数:int result = funcPtr(3, 4);

ps:在 C/C++ 中,函数名本身就代表了其地址,因此不需要使用取地址符 &


指针和成员访问

在 C++ 中,指针指向某个对象时,有两种方式可以访问该对象的成员:ListNode *cur = list;

1.使用解引用和点操作符:

(*cur).val;

这行代码首先解引用指针cur,然后通过.操作符访问val成员。虽然这在语法上是正确的,但相对较冗长

2.使用箭头操作符:

cur->val;

箭头操作符 -> 是专门为指向对象的指针设计的,它同时完成解引用和成员访问的操作


深拷贝 / 浅拷贝

深拷贝和浅拷贝是对象复制的两种不同方式,它们的主要区别在于如何处理对象内部的指针和动态分配的内存

1. 浅拷贝(Shallow Copy)

浅拷贝会复制对象的所有成员,包括指针。对于指针成员,浅拷贝仅复制指针的值(地址),而不复制指针指向的对象

  • 复制后,源对象和目标对象指向同一块内存区域
  • 如果其中一个对象被修改,另一个对象也会受到影响,因为它们共享同一数据
  • 当两个对象被销毁时,它们都会尝试释放相同的内存区域,可能导致双重释放(double free)的问题,进而引发程序崩溃或未定义行为

2. 深拷贝(Deep Copy)

深拷贝会复制对象的所有成员,包括指针指向的内存区域。对于指针成员,深拷贝不仅复制指针的值,还会为指针指向的对象分配新的内存空间,并复制其内容

  • 复制后,源对象和目标对象各自拥有独立的内存区域
  • 修改一个对象不会影响另一个对象,因为它们有不同的数据副本
  • 需要确保在析构函数中正确释放每个对象的动态内存,以避免内存泄漏
  • 在深拷贝时,新的内存通常是通过 new 操作符分配在堆上,而不是栈上

优先队列 / 堆

堆(Heap):

  • 堆是一种特定的数据结构,通常用于实现优先队列
  • 堆可以是最大堆或最小堆,最大堆中的每个节点的值都大于或等于其子节点的值,而最小堆则相反
  • 堆的性质使得插入和删除最大(或最小)元素的操作都能在对数时间复杂度内完成

优先队列(Priority Queue):

  • 优先队列是一种抽象数据类型,支持按优先级排序元素的操作
  • 与普通队列不同,在优先队列中,元素的出队顺序是基于它们的优先级,而不是它们被插入的顺序
  • 常见操作包括插入元素(push)、获取并删除最高优先级元素(pop)和查看最高优先级元素(top
  • 优先队列通常使用堆作为其内部数据结构来高效地支持优先级操作
  • 在 C++ STL 中,std::priority_queue 就是基于堆的实现
  • 优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系


不准投币喔 👆

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇