1.类型推导(auto/declitype)

类型推导是指编译器根据上下代码自动推断变量、表达式或函数返回值的数据类型,无需开发者显式声明其类型的特性。这一特性可简化代码书写,减少冗余,同时保持类型安全。

1.1 auto

auto关键字用于推断变量类型,编译器根据初始化值自动确定类型。

auto x=42;//推导为int
auto str="hello"//推导为const char*
auto vec=std::vector<int>{1,2,3};//推导为std::vector<int>

1.2 decltype

decltype关键字用于推导表达式的类型(但不执行表达式),常用于声明与已有变量/表达式同类型的变量。

实例:获取表达式的类型

int num = 100;// 用 num 的类型(int)来声明 a
decltype(num) a;  // 等价于 int a;
a = 200;  // 正确,因为 a 是 int 类型

1.3 模板类型推导

编译器在实例化模板时,自动根据传入的实参推导模板参数的类型。

template<typename T>
void print(T value){
   std::cout<<value<<std::endl;
}

print(42);//推导T为int
print("asd");//推导T为const char*

1.4 C++14后的函数返回类型推导

用auto作为函数返回类型,编译器根据return语句自动推导。

auto add(int a,double b){
    return a+b;//推导类型为double
}

1.for循环

C++语言当中可以遍历数组、容器、string以及由begin/end函数定义的序列,这得益于迭代器和范围for循环的设计,体现“泛型编程”的思想。

1.迭代器遍历(适用于所有支持begin()/end()的序列)

通过调用序列的begin()(返回首个元素的迭代器)和end()(返回尾后位置的迭代器),使用迭代器访问元素好处是语法统一。

int mian(){
  //数组
  int arr[]={1,2,3,4};
  for(auto it=std::begin(arr);it!=std::end(arr);++it){
  std::cout<<*it;
  }
  
  //vector容器
  std::vector<std::string>vec={"a","b","c"};
  for(auto it=vec.begin();it!=vec.end;++it){
    std::cout<<*it;
  
  return 0;
}

2.范围for循环(简化迭代器遍历)

直接遍历序列中的每个元素,底层自动调用begin()/end(),语法简洁。

vector<double>vec={1,2,3,4,5,6,7,8};
for(double var:vec){
  cout<<val<<endl;
}

begin()/end()接口:数组(通过标准库函数begin/end)、STL容器(成员函数begin/end)、string(成员函数)都提供了这对接口,返回的迭代器支持++(移动到下一个元素)和!=(判断是否结束)操作。

3.lambda表达式

lambda表达式是c++11引入的匿名函数(无函数名的函数),可在需要函数的地方就地定义,简化代码编写。

3.1最简单的lambda(无参数、无捕获)

int main(){
  //定义并立即调用lambda
  []{
       cout<<"hello"<<endl;
  }();
 return 0;
}

3.2带参数和返回值的lambda

int main(){
  //计算两数之和的lambda
  auto add=[](int a,int b){
    return a+b;
  };
  cout<<and(3,5)<<endl;
  return 0;

3.3捕获外部变量(关键特性)

int main(){
  int base=10;
  //值捕获:捕获base的副本(内部修改不影响外部)
  auto add_base=[base](int x){
    return base+x;
  };

  //引用捕获
  auto modify_base=[&base](int x){
    base=x;
  };

  cout<<add_base(5)<<endl;//输出:15
  modify_base(20);
  cout<<base<<endl;//输出:20

  return 0;
}

lambda核心价值是“就地定义简短函数”,适合临时需要一个小函数的场景。通过捕获列表,能灵活访问外部变量,比普通函数更简洁,比指针函数更易用。

4.构造函数

构造函数是用于初始化类对象的特殊成员函数。委托构造和继承构造是两种简化构造函数定义的语法特性,分别用于同一类内部和父子类之间的构造逻辑复用。

4.1委托构造

允许一个构造函数调用同一类中的另一个构造函数,避免重复编写初始化逻辑。

当类有多个构造函数,且它们的初始化逻辑存在重叠时,可通过“委托”减少代码冗余。

class Student {
private:
    std::string name;
    int age;
    int score;

public:
    // 基础构造函数(被委托的目标)
    Student(std::string n, int a, int s) 
        : name(n), age(a), score(s) {}

    // 委托构造:调用上面的基础构造函数,补充默认值
    Student(std::string n, int a) 
        : Student(n, a, 0) {} // 委托给带3个参数的构造函数

    // 再委托:调用带2个参数的构造函数,补充默认值
    Student(std::string n) 
        : Student(n, 18) {} // 间接委托给基础构造函数
};

委托构造通过成员初始化列表调用同类的其他构造函数(不能在函数体中调用),避免了重复编写name=n;age=a;等初始化代码。

4.2继承构造

允许派生类直接“继承”基类的构造函数,无需手动重写即可使用基类的初始化逻辑。使用场景是:当派生类不需要额外的成员变量初始化,或仅仅需复用基类的构造函数时,简化派生类构造函数的定义。

实例1

class Base{
private:
  int x;
  int y;
public:
  Base(int a):x(a),y(0){}//基类构造函数1
  Base(int a,int b):x(a),y(b){}//基类构造函数2
};

class Derived:public Base{
pulic:
  //继承构造:使用using声明继承基类的所有构造函数
  using Base::Base;
  //派生类可额外定义自己的构造函数(不会覆盖继承的构造函数)
  Derived():Base(0,0){}
};
 int main(){
   Derived d1(10);//调用继承的Base(int a)
   Derived d2(20,30);//调用继承的Base(int a,int b)
   Derived d3;//调用派生类自己的默认构造函数
   return 0;
}

实例2

#include <string>
using namespace std;

// 基类:人
class Person {
public:
    // 基类的构造函数1:只初始化名字
    Person(string n) : name(n) {}
    
    // 基类的构造函数2:初始化名字和年龄
    Person(string n, int a) : name(n), age(a) {}

    string name;
    int age = 0; // 默认年龄0
};

// 派生类:学生(继承自人)
class Student : public Person {
public:
    // 关键:用 using 声明继承基类的所有构造函数
    using Person::Person; 

    // 派生类自己的成员(可以默认初始化)
    int score = 0; 
};

int main() {
    // 用基类的构造函数1初始化 Student 对象
    Student s1("张三"); 
    // s1.name = "张三"(继承自 Person 的构造函数),s1.age=0(默认),s1.score=0(默认)

    // 用基类的构造函数2初始化 Student 对象
    Student s2("李四", 18); 
    // s2.name="李四",s2.age=18(继承自 Person 的构造函数),s2.score=0(默认)

    return 0;
}

若新增成员需要初始化,可在派生类中额外定义构造函数,不会影响继承的基类构造函数。

class Student : public Person {
public:
    using Person::Person; // 继承基类构造函数
    
    // 派生类自己的构造函数(处理新增成员)
    Student(string n, int a, int s) : Person(n, a), score(s) {}

    int score;
};

5.array/forward_list/tuple容器

1.array(固定大小数组容器)

封装了固定大小的原生数组,是C风格数组的“现代化包装”,定义于<array>头文件,编译器确定大小(如array<int,5>表示含5个int的数组),不可动态扩容/缩容,元素在内存中连续存储,支持随机访问(通过下标[]或at());提供begin()/end()等迭代器接口,可配合STL算法(如sort、for_each)使用。

适用场景:需要固定大小、高效访问的数组场景(如存储已知长度的顶配项、坐标等),替代C风格数组以增强安全性。

#include<array>
#include<iostream>
using namespace std

int mian(){
  array<int,3>arr={1,2,3};
  for(int num:arr){
    cout<<num<<endl;
  }

2.forward_list(单向链表)

本质是单向链表,定义于<forward_list>头文件,仅继承单向遍历(从头部到尾部)。元素数量可动态增减,内存不连续,在头部或已知位置(通过迭代器)插入/删除元素,仅支持forward_iterator(前向迭代器),只能用++移动,不支持--或随机访问(如it+2无效);相比于list,每个节点少一个前向指针,内存占用更少。

适用场景:需频繁在头部或中间插入/删除元素,且只需要单向遍历的场景(如实现栈、简单队列)。

int mian(){
  forward_list<int>flist={1,2,3};
  flist.push_front(0);//头部插入0
  for(int num:flist){
    cout<<num<<endl;
  }
return 0
}

5.3tuple(元组)

可存储多个不同类型数据的“打包容器”,定义于<tuple>头文件,类似于“固定大小的异构集合”。

可包含不同类型的数据(如tuple<int,string,double>可存int、string

double);编译器确定元素类型和数量,不可动态增减元素‘通过get<索引>(tuple)访问元素,或用tie解包到变量;常用于函数返回多个值、存储不同类型的关联数据(如键值对扩展、记录不同类型的信息)。

适用场景:需要一次传递或存储多个不同类型数据的场景(如函数返回“状态码+消息+结果值”)。

int mian(){
  tuple<int,string,double>t(10,"hello",3.14);
  cout<<get<0>(t)<<endl;//访问第0个元素
  cout<<get<1>(t)<<endl;//访问第1个元素

  //解包到变量
  int a;sting b;double c;
  tie(a,b,c)=t;//a=10,b="hello",c=3.14
  return 0;
}

根据具体需求选择:同类型+固定大小用array,同类型+动态单向链表用forward_list,不同类型打包用tuple.

Logo

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

更多推荐