目录
C++11介绍
C++11是指C++编程语言的第11个版本,它于2011年发布。
C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多。
更多详情,可以去官网了解:C++11 - cppreference.com
列表初始化
{}初始化
C++98中,标准允许使用
{}
花括号对数组或者结构体元素进行统一的列表初始值设定
struct Point
{
int x;
int y;
};
int main()
{
//对数组进行初始化
int array1[] = { 1, 2, 3, 4, 5 };
int array2[5] = { 0 };
//对结构体元素进行初始化
Point p = { 1, 2 };
return 0;
}
C++11扩大了用大括号括起的列表(初始化列表)的使用范围 使其可以用于所有内置和用户自定义类型。使用初始化列表时,可以添加
=
等号,也可不添加
struct Point
{
int x;
int y;
};
int main()
{
// 内置类型初始化
int x = { 10 }; // -> int x = 10
int x2{ 10 }; // 可省略等号
// 用户自定义初始化
Point p1{ 3,5 };// 可省略等号
//使用大括号对数组元素进行初始化
int array1[]{1, 2, 3, 4, 5}; //可不添加等号
int array2[5]{0}; //可不添加等号
// c++11中列表初始化也可以用于new表达式(c++98不可用)
int* p1 = new int[5]{ 0 };
int* p2 = new int[5]{ 1,2,3,4,5 };
//初始化标准库
vector<int> v = { 1,2,3,4,5,6,7 };
list<int> l{ 1,2,3,4,5 };
map<int, double> myMap = { {1,1.0},{2,2.0} };
return 0;
}
创建对象是,使用列表初始化方式调用构造函数
class Date
{
public:
Date(int year, int month, int day)
:_year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
int main()
{
//一般调用构造函数创建对象的方式
Date d1(2022, 8, 29);
//C++11支持的列表初始化,会调用构造函数初始化
Date d2 = { 2022, 8, 30 }; //可添加等号
Date d3{ 2022, 8, 31 }; //可不添加等号
return 0;
}
std::initializer_list
initializer_list是C++11引入的一种特殊类型,用于在对象构造过程中提供初始化列表
{}
大括号括起的列表(初始化列表),使用auto自动类型推导,与typeid(变量名).name()
的方式可以查看器类型
initializer_list是让其他容器支持列表初始化的,没有增删查改的接口
C++11给标准库中的容器增加了新的构造函数,以initalizer_list为参数
如vector容器:
当使用{}
列表对容器进行初始化时,会识别成initializer_list类型,然后调用对应的构造函数对容器进行初始化
使用示例,模拟实现vector(initializer_list il):
namespace myVector
{
template<class T>
class vector {
public:
typedef T* iterator;
// 使用 initializer_list 进行初始化
vector(initializer_list<T> l)
{
_start = new T[l.size()];
_finish = _start + l.size();
_endofstorage = _start + l.size();
iterator vit = _start;
//typename 指明iterator为类型而非静态成员之类的
/*typename initializer_list<T>::iterator lit = l.begin();
while (lit != l.end()
{
*vit++ = *lit++;
}*/
for (auto e : l)
*vit++ = e;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
通过在容器的构造函数中遍历initializer_list元素,完成初始化工作。
此外,赋值运算符重载同理
vector<int> v;
v = { 1,2,3,4,5 };
变量类型推导
auto
在 c++11 中,auto
被重新定义为一个用于类型推导的关键字。
auto
主要用于让编译器根据初始值来确定变量的类型,简化代码的书写,减少类型重复,提高代码的可读性
int main()
{
int i = 10;
auto p = &i;
auto pf = string("123");
for(auto e : string){}
return 0;
}
auto不仅可以减少代码的冗余,还能够避免自主声明类型可能导致的错误。
decltype
C++11中引入的关键字,用于获取表达式的类型。decltype(expression)
int x = 5;
decltype(x) y; // 推断y的类型为int
double func(int a, double b);
decltype(func(1, 2.0)) z; // 推断z的类型为double,
int array[] = {1, 2, 3};
decltype(array) arr; // 推断arr的类型为int[3]
decltype并不会执行表达式,在编译时分析表达式类型。
decltype特别是在模板编程中很有用,可以使代码更加灵活,更准确处理各种类型推断。
template<class T1, class T2>
// 推导函数的返回值类型
auto fun(T1 x, T2 y)
{
return x + y;
}
int main()
{
decltype(fun(1, 2)) ret1;
cout << typeid(ret1).name() << endl; // int
decltype(fun(1.1, 2.2)) ret2;
cout << typeid(ret2).name() << endl; // double
return 0;
}
typeid(变量名).name()
只是用于获取变量类型,并以string返回字符串,无法通过其来定义变量。
STL中的改变
array
本质是静态数组,即定长数组
//定义一个可存储10个int类型元素的array容器
array<int, 10> a1;
- arrary和普通数组一样,可以使用
[]
访问下标元素,创建后数组大小不能改变 - arrary容器用类对数组进行封装,访问元素时会进行越界检查,
[]
是断言检查,at()
是抛异常检查。对于普通数组,只有在写操作时会报错。
forword_list
单链表
参考:文档
使用很少
unordered_map与unordered_set
底层都使用哈希表,使用上与map和set差别不大
参考:详细文档
内置类型转string
可调用同一的函数to_string
string转内置类型
通过调用string类中对应的成员方法
类的新功能
默认成员函数
在C++11前,每个类有6个默认成员函数:构造函数、析构函数、拷贝构造函数、拷贝赋值函数、取地址重载函数和const取地址重载函数。这六个函数中,前4个比较重要,后2个一般不会显示声明。默认,即如果没有声明定义,编译器会自动生成。
C++11标准新增两个默认成员函数:移动构造函数、移动赋值运算符重载
对于这两个函数需要注意:
- 移动构造函数的默认生成条件:自己没有实现移动构造函数,且没有实现析构函数、拷贝构造和拷贝赋值重载。
- 移动赋值重载函数的生成条件:自己没有实现移动赋值重载函数,且没有实现析构函数、拷贝构造函数和拷贝赋值函数。
对于默认生成的移动构造函数:内置类型会执行逐成员按字节拷贝;自定义类型成员如果实现了就调用移动构造,没有实现就调用拷贝构造。
默认移动赋值和移动构造完全类似。
参考:右值引用与移动构造
成员变量缺省值初始化
默认的构造函数,对于内置类型是不做初始化的。于是C++11支持非静态成员变量在声明时使用缺省值,默认生成的构造函数会使用这些缺省值对成员进行初始化。
class Point
{
private:
//声明缺省值
int _x = 0;
int _y = 0;
}
此操作并不是定义成员变量,而只是进行声明,使用默认构造函数时才创建
default
default
关键字用于指示编译器生成默认的成员函数
示例:
类中已经定义了其他构造函数,也可以使用 default
关键字显式声明并定义一个默认构造函数
class Point
{
private:
Point(int x, int y)
:_x(x), _y(y)
{}
Point() = default;//显式声明默认构造函数
//Point() {};
private:
//声明缺省值
int _x = 0;
int _y = 0;
}
需要传参的构造函数与不需要传参的构造函数构成重载。当然,某些情况下,手动编写成员函可能更合适。
显示声明默认移动构造函数和移动赋值运算符重载
class MyClass {
public:
Point(Point&& obj) = default;
Point& operator=(Point&& obj) = default;
};
注意:default只能用于8种默认成员函数。
delete
delete
关键字,可以用于删除这8中默认函数。
C++98中,可以将该函数设置成私有,并且只用声明不用定义,这样当外部调用该函数时就会报错
C++11时,在函数声明后加上=delete
,用于删除类的默认成员函数,使其不能被调用
class Point
{
public:
Point& operator=(const Point& ) = delete;//禁止调用赋值运算符
~Point() = delete;//禁止调用析构函数
};
如果调用这些函数,编译时会报错,如:尝试引用已删除的函数
此外,delete
关键字也可以用于删除某个函数的重载版本
void foo(int) {
// 这个重载版本是可用的
}
void foo(double) = delete; // 删除double类型的重载版本
int main() {
foo(10); // 调用foo(int)
foo(3.14); // 编译错误,无法调用被删除的重载版本
return 0;
}
final与override
final
关键字用于声明类不可被继承
class Base final {
// ...
};
// 编译错误,无法从final类派生出新的子类
class Derived : public Base {
// ...
};
final
关键字用于声明虚函数不可被重写
class Base {
public:
virtual void foo() final {
// ...
}
};
class Derived : public Base {
public:
// 编译错误,无法重写被标记为final的成员函数
void foo(){
// ...
}
};
override
关键字用于表示派生类的成员函数重写(覆盖)了基类的虚函数。
它是一种编译期的标记,可以提高代码的可读性和安全性。
class Base {
public:
virtual void foo() {
// ...
}
};
class Derived : public Base {
public:
void foo() override { // 使用override关键字重写基类的虚函数
// ...
}
};
会检查子类是否,重写了父类的某个虚函数,如果没有重写则编译会报错
🦀🦀观看~~