C++标准特性1(类型推导、for循环、lambda表达式、构造函数、arrayforward_listtuple容器)
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.
更多推荐



所有评论(0)