1.C++的初始化:
1.1C++98中初始化问题
在C++98中,能用大括号对数组元素初始化,如下:
int a[] = {1,2,3,4}
但是不能对自定义类型却不能用大括号初始化,如下
vector<int> v{1,2,3,4};
上面的初始化在C++98中是不允许的,但是在C++11中扩大了大括号括起的列表的作用范围,使其可以用于所有的内置类型和自定义类型,在使用初始化列表时也可以用 = ,意义与使用{}初始化一致。如下:
vector<int> v = {1,2,3,4};
2.2内置类型的列表初始化
//变量
int x1 = { 10 };
int x2{ 10 };
int x3 = 1 + 2;
int x4{ 1 + 2 };
int x5 = { 1 + 2 };
//数组
int a1[5] = { 1, 2, 3, 4, 5 };
int a2[] = { 1, 2, 3, 4, 5 };
int* a3 = new int[5] {1, 2, 3, 4, 5};
//STL容器
vector<int> v{ 1, 2, 3, 4 };
map<string, int> m{ { "苹果", 5 }, { "桃子", 10 } };
1.3自定义类型的初始化
- 1.单个对象的列表初始化
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 d = { 1999, 6, 8 };
//Date d{1999, 6, 8};
return 0;
}
- 2多个对象的列表初始化
多个对象想要支持列表初始化,需给该类添加一个带有initializer_list类型的构造函数即可。
2.变量类型推导(auto, decltype)
2.1为什么要进行类型推导?
在定义类型的时候,必须先给出变量的实际类型,编译器才能定义,但有些时候不知道实际类型该怎么给,或者实际类型非常复杂(迭代器),比如:
std::map<std::string, std::int> m{ { "苹果", 5 }, { "桃子", 10 } };
std::map<std::string, std::int> ::iterator it = m.begin();
auto it = m.begin();
上列两个句子的意思是一样的,但是谁比较复杂一眼便知。
C++新增了auto用于根据变量初始化表达式类型推导变量的实际类型。
2.2 decltype类型推导
-
1为什么要用decltype
auto使用的前提:必须对auto声明的类型进行初始化,否则编译器无法导出auto实际类型。但有些时候可能需要根据表达式运行完成之后的结果的类型进行推导,因为在编译期间,代码不会运行,此时auto就无能为力了。因此C++11中就加入了一种能用与加完之后结果的实际类型作为函数的返回值类型就不会出错decltype也因此被创建。
缺点:效率偏低- 2.decltype
decltype是根据表达式的实际类型推演出定义变量时所用的类型即decltype是通过表达式运行结果的返回类型来定义我们要推导的类型的。
1.推演表达式类型作为变量的定义类型
short a = 32670; short b = 32670; decltype(a + b) c; cout << typeid(c).name() << endl;
结果:
2.推演函数的返回值类型void* ALA(int size) { return malloc(size); } int main() { //没有带参数,推演函数的类型 cout << typeid(decltype(ALA)).name() << endl; //带了参数,推导的是函数返回值的类型,注意:此处只是推演,不 执行函数 cout << typeid(decltype(ALA(0))).name() << endl;
结果:
- 2.decltype
3.范围for
3.1基于范围的for循环
for(元素类型 元素对象:容器对象)
{
循环体
}
(1.1)如果循环体由单条语句或者单个结构块组成,可以省略花括号
(1.2)用元素对象依次结合容器对象中的每一个元素,每结合一个元素,执行依次循环体,直至容器内的所有元素都被结合完为止.
(1.3)不依赖于下标元素,通用
(1.4)不需要访问迭代器,透明
(1.5)不需要定义处理函数,简洁
注意事项:
(2.1)对map和multimap容器使用范围循环,每次拿到的元素既不是键也不是值,而是由键和值组成的pair
(2.2)在使用基于范围的for循环时,不能违背容器本身的约束
(2.3)基于范围的for循环,无论循环体执行多少次,冒号后面的表达式永远只执行一次
(2.4)基于范围的for循环,其底层实现依然要借助于容器的迭代器,
4.final和override
5.智能指针
C++第十二天:智能指针(shared_ptr, unique_ptr,auto_ptr 明理和模拟实现)
6.新增加容器array、forward_list以及unordered系列
7.委派构造函数
7.1构造函数冗余造成的重复
委派构造函数目的是为了减少程序员书写构造函数的时间。通过委派其他构造函数,多构造函数的编写会更加简单
class Date
{
public:
//目标构造函数
Date()
:_year(0),
_month(0),
_day(0)
{}
//委派构造函数
Date(int year)
:Date()
{
_year = year;
}
//委派构造函数
Date(int month)
:Date()
{
_month = month;
}
private:
void InitRset(){} //用于初始化其他变量
private:
int _year = 1999;
int _month = 6;
int _day = 8;
};
在初始化列表中调用“基准版本”的构造函数是委派构造函数。
8.函数控制(delete,defalut)
在C++中空类编译器会生成一系列默认成员函数。如果这些成员函数已经在类中被显示定义,则编译器不会重新生成默认成员函数。于是C++11为了让程序员可以控制编译器是否生成,设立了两个关键字(delete.default)
8.1显示缺省函数
在默认函数定义或声明附加上=default,从而显示的指示编译器生成函数的默认模板。用default修饰的函数为显示缺省函数
class A
{
public:
A(int a)
:_a(a)
{}
//显示缺省函数,由编译器生成
A() = default;
//在类中声明,在类外定义时让编译器生成默认赋值运算符重载
A& operator= (const A& a);
private:
int _a;
};
A& A::operator=(const A& a) = default;
int main()
{
A a1(10);
A a2;//如果没有显示缺省函数,这一步会失效
a2 = a1;
return 0;
}
8.2删除默认函数
如果想要限定某些默认函数生成,在C++98中,是该函数设置成private,并且不给定义。但在C++11中则方便了很多,只需在函数声明时加上=delete即可。
class A
{
public:
A(int a)
:_a(a)
{}
//显示缺省函数,由编译器生成
A() = delete;
//在类中声明,在类外定义时让编译器生成默认赋值运算符重载
A& operator= (const A& a) = delete;
private:
int _a;
};
int main()
{
A a1(10);
//失败,因为没有隐式的构造函数
//A a2;
//失败,因为没有赋值运算符重载
//a2 = a1;
return 0;
}
9.右值引用
9.1C++11中的右值
什么是左值?
(=)左边的就是左值,左值是可以改变的值
什么是右值?
(=)右边的不一定是右值,右值是不可改变的值(纯右值,将亡值)
- 纯右值是
C++98中右值的概念,用于识别临时变量和一些不跟对象关联的值。 - 将亡值
声明周期将要结束的对象。
9.2右值引用: - 书写格式:
类型 && 变量名称 = 实体;
int main()
{
int a = 10;
int &lr = a;
int&& rr = 10;//
return 0;
}
- 两个常见的地方:
- 1.与移动语义相结合,减少务必要的资源开辟来提高代码的效率
- 2.给一个匿名对象取别名,增长匿名对象的资源生命周期。
- 与引用一样,右值的初始值在定义时必须初始化
- 通常情况下,右值引用不能引用左值(除非右值+const)
9.2move()
位于头文件,唯一的作用就是将一个左值强制转化为右值引用,通过右值引用使用该值,实现移动语义。
## 10.lambd
10.1 C,C++98,C++11的函数用法(函数指针,仿函数,lambd用法)对比:
- C:函数指针
```cpp
typedef bool(*ALA)(int, int);
bool func1(int i, int j)
{
return i > j;
}
int main()
{
ALA a = func1;
bool(*a)(int, int) = func1;
cout << a(1, 2) << endl;
system("pause");
return 0;
}
缺点:类型定义难理解
- C++98:仿函数(运算符重载)
struct func2
{
public:
bool operator()(int i, int j)
{
return i > j;
}
};
int main()
{
func2 f2;
cout << f2(1, 2) << endl;
system("pause");
return 0;
}
缺点:先定义好再使用
- lambda表达式:
书写格式::[capture-list] (parameters) mutable -> return-type { statement }auto p3 = [](int i, int j)->bool{return i > j; }; cout << p3(1,2) << endl;
- 结构:
1.[ ]:捕捉列表,建议其根据[]来判断接下来的函数是否是lambda函数,捕捉列表能够捕捉上下文的变量供lambda函数使用(传引用)
2.():参数列表,与普通函数一样,如果不需要传参,可忽略。
3.mutable:默认情况下,lambda函数是一个const函数,mutable可以取消其常量性。使用该参数的时候,参数列表不可忽略。
4.->return_type:返回值类型。没有返回值类型可以省略,返回值类型明确的情况下,也可省略,由编译器进行推导
5.{ }函数体。再该函数体内,除参数外,还可以使用捕捉列表里的数。
具体例子:
- 结构:
struct Goods
{
string _name;
double _price;
double _appraise;
};
int main()
{
Goods gds[] = { { "苹果", 2.1, 10 }, { "相交", 3, 8 }, { "橙子", 2.2, 7 }, { "菠萝", 1.5, 10 } };
sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods& g1, const Goods& g2)->bool
{return g1._price > g2._price; });
sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods& g1, const Goods& g2)->bool
{return g1._appraise > g2._appraise; });
return 0;
}