在这里插入图片描述

大家好,我是Halcyon.平安

欢迎文末添加好友交流,共同进步!

在这里插入图片描述


目录

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

  • 值传递:值传递会触发一次拷贝的动作,并且对参数的修改不会影响原始值。对象大的话效率低。
  • 引用传递:只是一个参数的引用,不会触发拷贝动作,效率高。但是修改参数会直接作用于原始值。(大对象推荐用 const 引用,既能避免拷贝又不会被修改)

C与C++的区别

六个方面:

  • 面向过程与面向对象:
    • C语言是面向过程的,主要是通过函数来解决问题(自己造轮子)
    • C++主要是面向对象的,主要是使用类和对象组织代码(使用造好的轮子)
  • 继承:
    • C语言没有继承的概念
    • C++允许继承,且可以一个子类继承多个父类
  • 函数重载:
    • C语言不支持函数重载,函数名必须不一样
    • C++支持函数重载(支持类型和参数个数不同的重载)
  • 模板:
    • C语言不支持模板
    • C++支持模板,且有静态和动态的多态
  • 内存管理:
    • C语言使用的是malloc与free,用于申请和释放内存
    • C++使用的是new和delete进行内存管理(也可以用智能指针)
  • 标准库:
    • C++的标准库比C语言的标准库丰富很多,比如vector/string/map等等

什么是C++的左值和右值?它们有什么区别?

  • 左值:赋值运算符的左边,有自己的地址空间,一般是有名字的变量
  • 右值:赋值运算符的右边,没有自己的地址空间,一般是常量、临时值或表达式求值结果

区别:一个在等号左边一个在等号右边 / 一个可以取地址且有名字,另一个不可以取地址且没有名字

补充:C++11 把右值又细分为纯右值(prvalue)和将亡值(xvalue),了解即可。


什么是C++中的移动语义与完美转发

首先它们都是C++11引入的新特性!!!

  • 移动语义(std::move):常规的资源管理是拷贝别人的资源,而移动语义直接是转移资源的所有权,性能更好(移动构造和移动复制实现的)
#include <iostream>
#include <string>
using namespace std;

int main() {
    string a = "123";
    // 拷贝:复制一份字符串,两份内存
    string b = a;
    // move:把a的内存所有权转移给c,a变空(所以不能再随意读取了),无复制
    string c = move(a);

    cout << a << endl; // 空
    cout << b << endl; // 123
    cout << c << endl; // 123
    return 0;
}
  • 完美转发(std::forward):可以写一个接收任意实参的函数模板并且转发到其它函数(目标函数会收到与转发函数完全相同的实参,转发函数的实参是左值,那目标函数的实参也是左值;同理,转发函数实参是右值那目标函数实参也是右值)
#include <iostream>
using namespace std;

// 重载1:接收左值 int&
void test(int&)  { cout << "左值\n"; }
// 重载2:接收纯右值 int&&
void test(int&&) { cout << "右值\n"; }

// 模板函数,T&& 万能引用
template<typename T>
void f(T&& x) {
    test(forward<T>(x));
}

int main() {
    int x = 1;
    f(x);    // 传入左值变量
    f(100);  // 传入临时字面量右值
    return 0;
}

//输出结果:
//左值
//右值

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

这也是C++11新引入的,就是可以用{}进行初始化变量或者对象。并且可以防止类型窄化(避免丢失精度的隐式类型转化)

int a{10};
//////////////////////////////////
int arr[3] = {1,2,3};
/////////////////////////////////

class Point {
public:
	int x, y;
    Point(int a, int b) : x{a}, y{b} {}
};

Point p{1, 2}; // 使用花括号初始化对象

///////////////////////////////////
std::vector<int> vec = {1, 2, 3, 4};

//////////////////////////////////
int x{3.14}; // error,float转int会触发类型窄化


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

  • 作用:

    • 它是将对象的资源从一个对象转移到另一个对象(然后之前的对象就变为空),这样就无需进行深拷贝,减少资源内存的分配,提高性能!
  • 原理:

// 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);
}
1. `T&& t`是万能引用,并不是单纯的右值引用;

2. 传入左值的话,T推到左值为`x&`,参数变为`x& &&`,折叠后为`x&`(左值引用);传入右值的话,T推到类型为`x`,参数变为`x&&`

3. 然后我们看C++引用折叠标准:
   & + & → &
   & + && → &
   && + & → &
   && + && → &&

4. `cpp::remove_reference_t<T>` 去引用,把类型所有引用(左值 &、右值 &&)全部剥离,得到原始裸类型

5. move 函数返回值`cpp::remove_reference_t<T> &&`不管 T 原来带不带引用,先全部剥掉,再强行拼接右值引用 `&&`。
   所以返回值类型永远是:裸类型的右值引用 `X&&`。

所以我们从源码知道,就是无论输入的是左值还是右值,都会强制的转成右值。说白了,move 啥也没搬,只是个强制类型转换,真正的搬运活在移动构造/移动赋值里。


介绍C++三种智能指针的使用场景

  • std::unique_ptr:独占所有权指针,意思就是同一时间内只有一个unique_ptr指向一个特定的对象,这个指针被销毁的时候,指向的对象也会被销毁。
    • 场景:一个对象只能被一个指针所拥有的时候;需要自动管理资源(堆对象、文件句柄等)。
  • std::shared_ptr:共享所有权的指针,多个shared_ptr可以指向同一个对象。内部的引用计数来确保只有当最后一个指向对象的shared_ptr被销毁的时候对象才被销毁。
    • 场景:多个所有者共享对象的时候;需要通过复制构造函数或赋值操作符来复制智能指针时候。
  • std::weak_ptr:不拥有所有权的指针,用来解决共享智能指针中的循环引用问题。
    • 场景:解决共享智能指针中的循环引用问题;需要使用但不拥有由共享指针管理的对象的时候

C++11中有哪些常用的新特性

  • auto类型推导:
    • 这样就不用手写复杂类型了,编译器会直接根据右侧的表达式自动推导出变量类型!
  • 智能指针:
    • 独占、共享、无所有权(解决循环引用);自动释放堆内存!
  • std::thread:
    • 标准的多线程,这样我们就不再需要跨平台!
  • 左值右值
  • std::function和lambda表达式
    • function用来统一包装任意可调用的对象,可以解决回调统一存储的问题!
    • lambda语法:[捕获列表](参数)mutable -> 返回值 {函数体}
  • 移动语义、完美转发
  • RAII lock:
    • 资源在构造的时候获取,在析构的时候自动解锁!(lock_guard 不能手动解锁,unique_lock 可以)

C++中static的作用?什么场景下用static?

  • 作用:
    • 修饰局部变量:改变的是生命周期(存在静态区,函数结束也不销毁),但作用域还在函数内部,下次调用还能保持上次的值
    • 修饰全局变量/函数:把作用域限制在本文件内,不允许跨文件使用(也叫内部链接性)
    • 修饰类的成员变量/函数:让整个类的对象共享这一份成员,所有对象共用
  • 几个注意点:
    • 静态成员变量需要在类外定义(C++17 的 inline 除外)
    • 静态成员函数没有 this 指针,只能访问静态成员,不能访问普通成员

C++中const的作用?谈谈你对const的理解

  • 修饰变量:必须初始化,且之后不能再被修改,相当于只读
  • 修饰指针
    • const int* p:指针指向的数据不能改,指针本身可以改(常量指针)
    • int* const p:指针本身不能改,指向的数据可以改(指针常量)
    • 记忆口诀:const 在 * 左边修饰数据,在 * 右边修饰指针
  • 修饰引用const T&,常用于函数参数,避免拷贝又保证不被修改(最常用)
  • 修饰成员函数:写在函数声明后面,表示这个函数不会修改任何成员变量,const 对象只能调用 const 成员函数
  • const 重载:参数是 T&const T& 可以构成重载,成员函数的 const 版本和非 const 版本也可以构成重载
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐