c++那些你不知道的标准特性(非常重要)

一、类型推导(auto/decltype)

1.使用语法

auto 变量名称 = 值;
decltype (表达式) 变量名称[ = 值(可有可无) ];

auto varname = value;  //auto的语法格式
decltype(exp) varname [= value];  //decltype的语法格式

int a = 3;
auto c1 = a;
decltype(a) c2 = a;    

2.auto 和 decltype 都会自动推导出变量 varname 的类型

auto 根据=右边的初始值 value 推导出变量的类型;
decltype 根据 exp 表达式推导出变量的类型,跟=右边的 value 没有关系。

注意:auto 要求变量必须初始化,也就是在定义变量的同时必须给它赋值;而 decltype 不要求,初始化与否都不影响变量的类型。这很容易理解,因为 auto 是根据变量的初始值来推导出变量类型的,如果不初始化,变量的类型也就无法推导了。

3.总结

auto 将变量的类型和初始值绑定在一起,而 decltype 将变量的类型和初始值分开;虽然 auto 的书写更加简洁,但 decltype 的使用更加灵活。

二、序列for循环语句

1.概念

for 循环在c++语言中可以遍历数组、容器、string以及由begin/end函数定义的序列。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    for (int i : v) {
        std::cout << i << " ";
    }
    std::cout << std::endl;
    return 0;
}


for(const auto var : {1,5,02,36,42,15,89})
	cout<<var <<"" ;

在这个示例中,我们定义了一个包含整数的vector,然后使用range-based for循环遍历其中的元素。for循环中的int i是一个迭代变量,每次循环时都会被设置为vector中的下一个元素,直到遍历完整个vector。
在列子中,我们定义了一个 {1,5,02,36,42,15,89}数组,让他排列输出,并且添加了const,避免数组被修改。

2.与传统for对比

1.更加简洁和直观,代码可读性更高。
2.遍历序列时无需手动计数或指定迭代器,减少了出错的可能性。
3.可以用于所有支持迭代器的容器和序列,包括数组、vector、list、set、map等等。

3.使用自定义迭代器

#include <iostream>
#include <vector>

class MyVector {
public:
    MyVector(std::initializer_list<int> l) : data(l) {}
    
    class iterator {
    public:
        iterator(std::vector<int>::iterator it) : it(it) {}
        iterator operator++() { return ++it; }
        int& operator*() { return *it; }
        bool operator!=(const iterator& other) { return it != other.it; }
    private:
        std::vector<int>::iterator it;
    };
    
    iterator begin() { return iterator(data.begin()); }
    iterator end() { return iterator(data.end()); }
private:
    std::vector<int> data;
};

int main() {
    MyVector v = {1, 2, 3, 4, 5};
    for (int i : v) {
        std::cout << i << " ";
    }
    std::cout << std::endl;
    return 0;
}

我们定义了一个MyVector类,它包含一个std::vector类型的成员变量data,并定义了一个自定义类型的迭代器iterator。我们还在MyVector类中定义了begin()和end()函数,这两个函数分别返回MyVector类的迭代器的起始和结束位置。
最后,我们使用range-based for循环遍历MyVector类的对象,这个过程中,我们没有直接使用vector的迭代器,而是使用自定义的迭代器来实现遍历。

4.总结

range-based for循环是一种简洁而直观的方式来遍遍历序列或容器中的元素。使用它可以减少代码的复杂性和出错的可能性,特别是在遍历复杂的数据结构时

三、lambda表达式

1.概念

lambda表达式(也称为lambda函数)是在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法。通常,lambda用于封装传递给算法或异步方法的几行代码。

#include <algorithm>
#include <cmath>

void abssort(float* x, unsigned n) {
    std::sort(x, x + n,
        // Lambda expression begins
        [](float a, float b) {
            return (std::abs(a) < std::abs(b));
        } // end of lambda expression
    );
}

下面我将原本代码和lambda表达式进行对比

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std ; 

bool comparefunc(int x,int y)
{
	return x<y;
}

int main()
{
	vector<int> myvr{93,78,133,12,89};
	vector<int> levr(myvr);
	sort(myvr.begin(),myvr.end(),comparefunc);
	cout<<"结果"<<endl;
	for(int i : myvr)
		cout<<i<<"  ";
	cout<<endl;
	
	// 下面使用lambda表达式
	sort(levc.begin(),levc.end(),[](int a,int b)->bool {return a<b ;});
	cout<<"lambda表达式输出结果"<<endl;
	for(int il : levc)
	{	
		cout<< il << " ";
	}
	
	return 0;
}


2.定义

在这里插入图片描述
1.捕获列表。在C ++规范中也称为Lambda导入器, 捕获列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数,捕获列表能够捕捉上下文中的变量以供Lambda函数使用。
2.参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略。
3.可变规格。mutable修饰符, 默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)。
4.异常说明。用于Lamdba表达式内部函数抛出异常。
5.返回类型。 追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
6.lambda函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。

四、构造函数:委托构造和继承构造

1.委托构造函数

委托构造函数,可以使用当前的类的其他构造函数来协助当前构造函数的初始化操作。

普通构造和委托构造的区别:
1.他俩都是一个成员初始化值列表的一函数体;
2.委托函数的成员初始化值列表只有一个唯一的参数,就是构造函数。

当被委托构造函数当中函数体有代码,那么会先执行完函数体的代码,才会回来到委托构造函数。

#include <iostream>
#include<string>
using namespace std;

// 设计类
class Testc
{
public:
	// 普通构造函数
	Testc(string s, int d) : data(d), str(s)
	{
		cout << "执行普通构造函数" << endl;
	}

	//委托构造函数
	Testc(int d) : Testc("测试 int d ", d)
	{
		cout << "执行委托构造函数" << endl;
	}

	void printdata()const
	{
		cout << "str:: " << str << endl;
		cout << "data:: " << data << endl;
	}

private:
	int data;
	string str;
};


int main()
{
	Testc obj("测试普通构造", 250);
	obj.printdata();

	Testc obj2(890);
	obj2.printdata();

	return 0;
}

输出结果:

执行普通构造函数
str:: 测试普通构造
data:: 250
执行普通构造函数
执行委托构造函数
str:: 测试 int d
data:: 890

2、继承构造函数

在c++中的继承关系,只有虚函数可以被继承,而构造函数不可以是虚函数,所以构造函数不能被继承,但是我们可以通过其他收段,达到继承效果。

#include<iostream>
using namespace std;

struct	A
{
	void func(double d)
	{
		cout << "base : " << d << endl;
	}
};

struct  B : A
{
	using A::func; // c++ 11标准中使用using关键字特点,使构造函数可以被“继承”
	void func(int i)
	{
		cout << "派生类b::" << i << endl;
	}
};

int main()
{
	A a;
	a.func(78);

	B b;
	b.func(87);
	return 0;
}

输出结果:

base : 78
派生类b::87

总结:通过using Base::Base来继承父类的构造函数,也可以通过这种方法在子类中使用父类的隐藏函数(即与子类函数同名但不是虚函数)

五、容器(array、forward_list、tuple)

array容器

  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值