跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++

C++核心知识点全解析:值传递、左值右值与智能指针

C++核心知识点涵盖值传递与引用传递区别、C与C++差异、左值右值概念、移动语义与完美转发、列表初始化、std::move原理及三种智能指针使用场景。内容涉及内存管理、对象生命周期及STL特性,旨在帮助理解C++基础机制与性能优化。

雾岛听风发布于 2026/3/16更新于 2026/4/2615 浏览
C++核心知识点全解析:值传递、左值右值与智能指针

1. C++中值传递和引用传递的区别

1) 值传递: 在函数调用时,会触发一次参数的拷贝动作,所以对参数的修改不会影响原始的值。如果是较大的对象,复制整个对象,效率较低。

2) 引用传递: 函数调用时,函数接收的就是参数的引用,不会触发参数的拷贝动作,效率较高,但对参数的修改会直接作用于原始的值。


2. C 和 C++ 的区别

可以考虑从以下几个方面回答:

1) 面向对象还是面向过程:

  • C语言是一门面向过程的语言,侧重于通过过程 (函数) 来解决问题。
  • C++是一门多范式语言,主要支持面向对象,侧重于使用类和对象来组织代码。

2) 继承:

  • C++支持继承,允许一个子类继承一个或多个父类,达到代码复用的目的。
  • C语言中没有继承的概念。

3) 函数重载:

  • C++支持函数通过参数类型和参数个数的重载。
  • C语言不支持重载,函数名必须唯一才行。

4) 模板:

  • C++支持模板,支持静态和动态形式的多态。
  • C语言对此都不支持。

5) 内存管理:

  • C++使用 new 和 delete 操作符来管理内存,也支持使用智能指针来动态管理内存。
  • C语言需要使用 malloc 和 free 来申请和释放内存。

6) 标准库:

  • C++的 STL 标准库能力比 C 语言丰富的多,比如 vector、string、list、map 等等,还有很多算法相关的能力,这些 C 语言都没有。

3. 什么是 C++ 的左值和右值?有什么区别?

什么是左值?什么是右值?

  • 左值:可以出现在赋值运算符的左边,并且可以被取地址,通常是有名字的变量。
  • 右值:不能出现在赋值运算符的左边,不可以被取地址,表示一个具体的数据值,通常是常量、临时变量。

一般可以从两个方向区分左值和右值。

方向 1:

  • 左值:可以放到等号左边的东西叫左值。
  • 右值:不可以放到等号左边的东西就叫右值。

方向 2:

  • 左值:可以取地址并且有名字的东西就是左值。
  • 右值:不能取地址的没有名字的东西就是右值。

示例:

int a = b + c;

a是左值,有变量名,可以取地址,也可以放到等号左边,表达式 b+c 的返回值是右值,没有名字且不能取地址,&(b+c)不能通过编译,而且也不能放到等号左边。

int a = 4; // a是左值,4作为普通字面量是右值

4. 什么是 C++ 的移动语义和完美转发

移动语义

一种优化资源管理的机制。常规的资源管理是拷贝别人的资源。而移动语义是转移所有权,转移了资源而不是拷贝资源,性能会更好。

移动语义通常用于那些比较大的对象,搭配移动构造函数或移动赋值运算符来使用。

  {
:
    ( size) : (size) { data_ =  [size]; }
    () {}
    ( A& a) { size_ = a.size_; data_ =  [size_]; cout <<  << endl; }
    (A&& a) { ->data_ = a.data_; a.data_ = ; cout <<  << endl; }
    ~() {  (data_ != ) { [] data_; } }
     *data_;
     size_;
};

{
    ;
    A b = a;
    A c = std::(a); 
     ;
}
class
A
public
A
int
size_
new
int
A
A
const
new
int
"copy "
A
this
nullptr
"move "
A
if
nullptr
delete
int
int
int main()
A a(10)
move
// 调用移动构造函数
return
0

如果不使用 std::move,会有很大的拷贝代价,使用移动语义可以避免很多无用的拷贝,提供程序性能,C++所有的STL都实现了移动语义,方便我们使用。例如:

std::vector<string> vecs;
...
std::vector<string> vecm = std::move(vecs); // 免去很多拷贝
完美转发

完美转发指可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参,转发函数实参是左值那目标函数实参也是左值,转发函数实参是右值那目标函数实参也是右值。

那如何实现完美转发呢,答案是使用 std::forward,可参考以下代码:

void PrintV(int &t) { cout << "lvalue" << endl; }
void PrintV(int &&t) { cout << "rvalue" << endl; }
template<typename T>
void Test(T &&t) {
    PrintV(t);
    PrintV(std::forward<T>(t));
    PrintV(std::move(t));
}

int main() {
    Test(1); // lvalue rvalue rvalue
    int a = 1;
    Test(a); // lvalue lvalue rvalue
    Test(std::forward<int>(a)); // lvalue rvalue rvalue
    Test(std::forward<int&>(a)); // lvalue lvalue rvalue
    Test(std::forward<int&&>(a)); // lvalue rvalue rvalue
    return 0;
}

分析:

  • Test(1): 1 是右值,模板中 T&&t 这种为万能引用,右值 1 传到 Test 函数中变成了右值引用,但是调用 PrintV 时候,t 变成了左值,因为它变成了一个拥有名字的变量,所以打印 lvalue 而 PrintV(std::forward(t)) 时候,会进行完美转发,按照原来的类型转发,所以打印 rvalue,PrintV(std::move(t)) 毫无疑问会打印 rvalue。
  • Test(a): a 是左值,模板中 T&&这种为万能引用,左值 a 传到 Test 函数中变成了左值引用,所以有代码中打印。
  • Test(std::forward(a)): 转发为左值还是右值,依赖于 T,T 是左值那就转发为左值,T 是右值那就转发为右值。

5. 什么是 C++ 的列表初始化

C++11 中引入了列表初始化,它的语法比较简单,就是可以使用花括号 {} 来初始化变量或对象。列表初始化可以应用于内置类型、用户自定义类型 (类、结构体等) 以及其它容器等。它有以下几点好处:

  • 方便,基本上可以替代普通括号初始化
  • 可以使用初始化列表接受任意长度
  • 可以防止类型窄化,避免精度丢失的隐式类型转换

下面深入看下列表初始化的几个用法:

  1. 基础数据类型
int a{10}; // 列表初始化
int a = {19}; // 列表初始化(也可以不使用等号)
  1. 初始化数组
int arr[3] = {1, 2, 3}; // 使用花括号初始化数组
  1. 类对象初始化,构造函数需要支持列表初始化
class Point {
public:
    int x, y;
    Point(int a, int b) : x{a}, y{b} {}
};
Point p{1, 2}; // 使用花括号初始化对象
  1. 容器初始化
std::vector<int> vec = {1, 2, 3, 4};
  1. 防止类型窄化
int x{3.14}; // error,float 转 int 会触发类型窄化
  1. 聚合类型的列表初始化 聚合类型是指没有用户定义的构造函数、没有私有或受保护的非静态数据成员、没有基类以及没有虚函数的类、结构体或联合体。对于聚合类型,列表初始化会直接按顺序初始化其成员。
struct Aggregate {
    int a;
    double b;
};
Aggregate agg{1, 2.3}; // 初始化 a 为 1,b 为 2.3
扩展知识

什么是类型窄化?

  • 从浮点类型到整数类型的转换
  • 从 long double 到 double 或 float 的转换,以及从 double 到 float 的转换,除非源是常量表达式且不发生溢出
  • 从整数或无作用域枚举类型到不能表示原类型所有值的整数类型的转换,除非源是其值能完全存储于目标类型的常量表达式

示例代码:

int main() {
    int a = 1.2; // ok
    int b = {1.2}; // error
    float c = 1e70; // ok
    float d = {1e70}; // error
    float e = (unsigned long long)-1; // ok
    float f = {(unsigned long long)-1}; // error
    float g = (unsigned long long)1; // ok
    float h = {(unsigned long long)1}; // ok
    const int i = 1000;
    const int j = 2;
    char k = i; // ok
    char l = {i}; // error
    char m = j; // ok
    char n = {j}; // ok,因为是 const 类型,这里如果去掉 const 属性,也会报错
}
std::initializer_list

我们平时开发使用 STL 过程中可能发现它的初始化列表可以是任意长度,有没有想过它是怎么实现的?

答案是 std::initializer_list,看这段代码:

struct CustomVec {
    std::vector<int> data;
    CustomVec(std::initializer_list<int> list) {
        for (auto iter = list.begin(); iter != list.end(); ++iter) {
            data.push_back(*iter);
        }
    }
};

std::initializer_list,它可以接收任意长度的初始化列表,但是里面必须是相同类型 T,或者都可以转换为 T。


6. C++ 中 move 有什么作用?它的原理是什么?

move 是 C++11 引入的一个新特性,用来实现移动语义。它的主要作用是将对象的资源从一个对象转移到另一个对象,而无需进行深拷贝,减少了资源内存的分配,可提高性能。它的原理很简单,我们直接看它的源码实现:

// move
template <class T>
LIBC_INLINE constexpr cpp::remove_reference_t<T> &&move(T &&t) {
    return static_cast<typename cpp::remove_reference_t<T> &&>(t);
}

从源码中你可以看到,std::move 的作用只有一个,无论输入参数是左值还是右值,都强制转成右值。


7. 介绍 C++ 中三种智能指针的使用场景?

C++中的智能指针主要用于管理动态分配的内存,避免内存泄漏。 C++11 标准引入了三种主要的智能指针: std::unique_ptr, std::shared_ptr, std::weak_ptr

1. std::unique_ptr

std::unique_ptr 是一种独占所有权的智能指针,意味着同一时间内只能有一个 unique_ptr 指向一个特定的对象。当 unique_ptr 被销毁时,它所指向的对象也会被销毁。

使用场景:

  • 当你需要确保一个对象只被一个指针所拥有时。
  • 当你需要自动管理资源,如文件句柄或互斥锁时。

示例代码:

#include <iostream>
#include <memory>

class Test {
public:
    Test() { std::cout << "Test::Test()\n"; }
    ~Test() { std::cout << "Test::~Test()\n"; }
    void test() { std::cout << "Test::test()\n"; }
};

int main() {
    std::unique_ptr<Test> ptr(new Test());
    ptr->test();
    // 当 ptr 离开作用域时,它指向的对象会被自动销毁
    return 0;
}
2. std::shared_ptr

std::shared_ptr 是一种共享所有权的智能指针,多个 shared_ptr 可以指向同一个对象。内部使用引用计数来确保只有当最后一个指向对象的 shared_ptr 被销毁时,对象才会被销毁。

使用场景:

  • 当你需要在多个所有者之间共享对象时。
  • 当你需要通过复制构造函数或赋值操作符来复制智能指针时。

示例代码:

#include <iostream>
#include <memory>

class Test {
public:
    Test() { std::cout << "Test::Test()\n"; }
    ~Test() { std::cout << "Test::~Test()\n"; }
    void test() { std::cout << "Test::test()\n"; }
};

int main() {
    std::shared_ptr<Test> ptr1(new Test());
    std::shared_ptr<Test> ptr2 = ptr1;
    ptr1->test();
    // 当 ptr1 和 ptr2 离开作用域时,它们指向的对象会被自动销毁
    return 0;
}
3. std::weak_ptr

std::weak_ptr 是一种不拥有对象所有权的智能指针,它指向一个由 std::shared_ptr 管理的对象。weak_ptr 用于解决 shared_ptr 之间的循环引用问题。

使用场景:

  • 当你需要访问但不拥有由 shared_ptr 管理的对象时。
  • 当你需要解决 shared_ptr 之间的循环引用问题时。
  • 注意 weak_ptr 肯定要和 shared_ptr 搭配使用。

示例代码:

#include <iostream>
#include <memory>

class Test {
public:
    Test() { std::cout << "Test::Test()\n"; }
    ~Test() { std::cout << "Test::~Test()\n"; }
    void test() { std::cout << "Test::test()\n"; }
};

int main() {
    std::shared_ptr<Test> sharedPtr(new Test());
    std::weak_ptr<Test> weakPtr = sharedPtr;
    if (auto lockedSharedPtr = weakPtr.lock()) {
        lockedSharedPtr->test();
    }
    // 当 sharedPtr 离开作用域时,它指向的对象会被自动销毁
    return 0;
}

目录

  1. 1. C++中值传递和引用传递的区别
  2. 2. C 和 C++ 的区别
  3. 3. 什么是 C++ 的左值和右值?有什么区别?
  4. 4. 什么是 C++ 的移动语义和完美转发
  5. 移动语义
  6. 完美转发
  7. 5. 什么是 C++ 的列表初始化
  8. 扩展知识
  9. std::initializer_list
  10. 6. C++ 中 move 有什么作用?它的原理是什么?
  11. 7. 介绍 C++ 中三种智能指针的使用场景?
  12. 1. std::unique_ptr
  13. 2. std::shared_ptr
  14. 3. std::weak_ptr
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • MogFace 人脸检测 WebUI 快速部署与使用指南
  • OpenClaw 实战指南:配置第三方 API 接入大模型
  • QClaw 基于 AI 与 OCR 的微信红包管理场景应用解析
  • Flutter for OpenHarmony 实战:通义万相 AIGC 联调与相册持久化
  • DJI DroneID 信号解析工具与 SDR 分析指南
  • 二级Python考试简单应用题真题与参考代码
  • Linux 匿名管道通信:原理与代码实战
  • 机器人工具坐标系精准标定:资深工程师的高效实战流程
  • 大疆 RTK 无人机免像控与有像控精度实测对比
  • 轻小说机翻机器人:5 分钟搭建日语小说翻译工具
  • Python 异步编程进阶与 asyncio 高级应用
  • Java RESTful 接口开发最佳实践
  • Seedance 2.0 飞书机器人集成安全合规与零信任加固实践
  • SketchUp STL 插件使用指南:从建模到打印
  • faster-whisper 快速安装与使用指南:AI 语音识别
  • Agent 智能体开发框架选型指南:Code-Based、LangGraph 与 Workflows 对比
  • OpenAI DALL·E API 绘图实战:从零构建 AI 绘画应用
  • Flutter 组件 genkit 适配鸿蒙:AI 流式响应与提示词工程
  • Triton 异步推理深度解析:C++ 客户端高性能并发处理实战
  • 基于红黑树封装实现 C++ map 与 set 容器详解

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online