##C++11初认识
###(1)初始化
void test()
{
vector v1{1, 2, 3, 4, 5, 6 };
for(int i = 0; i < v1.size(); ++i)
{
cout<
结果:1 2 3 4 5 6
###(2)auto关键字
在之前,auto关键字被当做是存储类型的指示符,但是在C++11中
auto完全有了新的含义 ,auto 作为一个新的类型指示符,其声明的变量必须由编译器在编译期间推倒而来
下面的例子
void test_1()
{
int a = 10;
auto b = a;
auto c = 'c';
//auto d;
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
}
结果:i i c
我们发现auto在定义变量时必须初始化,需要根据后面的表达式来推断出变量类型,编译期间会将auto自动替换为对应的类型
###auto 的优势
(1)用于复杂类型定义
void test_2()
{
map m1{{1,2},{3,4},{5,6}};
//map::iterator it = m1.begin();
auto it = m1.begin();
while(it != m1.end())
{
cout<second<<" ";
it++;
}
cout<
结果:2 4 6
(2)避免在类型上声明的错误
void test_3()
{
const float pi = 3.14f;
double radis = 3.0;
auto c = 2 * pi * radis;
cout<
输出结果
结果:18.84 d
(3)for循环遍历
void test_4()
{
vector v1{"hello", "world", "!"};
for(auto item : v1)
{
cout<
结果:hello world !
#####auto 的优点和缺点
优点:简单方便
缺点: 可读性差(定义复杂类型时读者不易于观察到类型)
#####不适合用auto的场景
###(2)decltype
虽然C98中已经有了RTTI(运行时类型识别) 我们上面使用的typeid 和 dynamic_cast
但是都有其不足处,dynamic_cast 只能用在虚函数的继承体系中,typeid只能用来识别出类型,不能定义变量。
decltype定义变量不用初始化
void test_5()
{
int a = 10;
decltype (a) b;
b = 10;
cout<< a<< " "<
结果:10 10
推倒函数返回值类型
int add(int a, int b)
{
return a + b;
}
void test_6()
{
int a = 3, b = 6;
cout<
结果:i
###(3)lambda表达式
lambda表达式书写格式
//书写格式
[capture] (paramenters) mutable ->return-type {statement}
//[捕捉列表] 可以用来捕捉上下文中变量提供的lambda函数使用
//(参数列表)
//mutable 默认情况下lambda函数为一个const函数,若添加上mutable可以取消其常性,该时参数列表不可以为空
//return-type 返回值
//{statement}函数
//参数列表和返回值都是可选部分,没有可以不用写,而捕捉列表和函数体是可以为空的
//即最简单的lambda表达式为[]{};
捕捉列表的形式有以下几种:
[var] :表示值传递方式捕捉某个人变量var
[=] :表示以值传递方式捕获所有父作用域中的变量
[&var]:表示以引用传递捕捉所有父作用域中的某个变量
[&] :表示以引用的方式捕捉父作用域中的所有变量
[this]:值传递方式捕捉当前的this指针
当然上面的几种方式可以混合使用
举例说明
void test_8()
{
int a = 3,b = 4;
cout<<"a:"<
结果:
a:3b:4
a:4b:3
看上面的形式我们很容易就发现很类似于仿函数,看大牛的博客说,其实仿函数的实现就是用lambda的一种
###(4)默认函数控制
#####(1)显示缺省函数函数
指定默认构造函数
因为我们知道,当我们自己实现了构造函数就不会生成默认的构造函数
但是当使用的default 关键字的时候,就可以指定自己想要的默认构造函数
#####(2)删除函数
想要限制默认函数的生成
在单例模式下,我们要实现防拷贝和防赋值运算符重载,普通的做法是声明为私有,在C++11中就可以简单的实现了
class A
{
public:
A(int a):_a(a){}
A(const A & a) = delete;
private:
int _a;
};
void test_9()
{
A a1(25);
// A a2(a1); //这一行会报错,因为上面我们将拷贝构造声明为删除函数,起到了防拷贝的效果
}
删除函数还可以起到防止隐士类型转换的作用
class A
{
public:
A(int a):_a(a){}
A(char a) = delete;
A(const A & a) = delete;
private:
int _a;
};
void test_9()
{
A a1(25);
A a2('a');//这里会报错,因为上面将参数类型为char 的构造函数声明为删除函数,即起到了防止隐士类型转换的作用
}
###(5)final
final修饰一个虚函数时,表明该函数不可以被重写
class Base
{
public:
virtual void Func()final
{
cout << "Base::Func()" << endl;
}
};
class Derived : public Base
{
public:
// void Func() // 报错
// {
// cout << "Base::Func()" << endl;
// }
};
final修饰一个类的时候,表明该类不可以被继承
class Base final
{
virtual void Func()
{
cout << "Base::Func()" << endl;
}
};
///class Derived : public Base //报错
//{
// public:
// virtual void Func()
// {
// cout << "Base::Func()" << endl;
// }
//};
####(6)override
在子类的虚函数中使用,如果构成隐藏就报错
当子类中不小心重写了父类的非虚函数的时候,如果加上了override 就会报错
class Base
{
public:
virtual void Func()
{
cout << "Base::Func()" << endl;
}
virtual void Func_2()
{
cout << "Base::Func()" << endl;
}
void func()
{
//do something
}
};
class Derived : public Base
{
public:
virtual void Func() override
{
cout << "Base::Func()" << endl;
}
//构成重写,编译器会报错
// virtual void Func_2(int a) override
// {
// cout << "Base::Func()" << endl;
// }
// virtual void func() override
// {
// //do something
// }
};
###移动语义
很多时候当我们将一个返回一个对象时,中间要经过
1、先拷贝一个临时对象
2、再在接受返回值的地方进行一次拷贝
我们发现整个过程中我们进行了两次拷贝,并且中间的那次临时对象的生命周期是极其的短暂
在C++11中提出了移动语义的概念
例如在实现 运算符重载的过程中 operator+()
我们看到移动语义就像是将临时变量的构造函数偷走了,一种资源转移的,也就是移动语义的解释
要实现移动语义,必须要借助C++11中的右值引用来完成
String(String&& s)
: _str(s._str)
{
s._str = NULL;
}
先来看什么是左值和右值
一般来说说,可以取地址成功的就是左值,剩下的就是右值
C++11中的右值有两层含义
1、纯右值,C98中的概念,用于辨识临时变量和一些与对象没有关联的值
2、将亡值,C++11新增的跟右值引用相关的表达式,这些表达式通常是将要被 移动的对象
String ReturnRValue()
{
return String("1234");
}
void TestString()
{
String&& s = ReturnRValue();
}
上面的这个式子中我们发现,声明一个名为s 的右值引用,ReturnRValue()函数返回的右值在表达式语句结束 后,其生命周期也就终结,而右值引用的声明,该右值又”重获新生”, 其生命周期就与s的生命周期一样。
注意:
1、无论左值引用还是右值引用,都是引用,所以必须初始化,只是其引用对象的别名
2、通常情况下,右值引用是不能绑定到任何左值
std::move()
函数会强制的将其转换为右值
但是一般不建议随便使用,一般用在将亡值
String s1("hello world");
String s2(move(s1));
String s3(s2);
上面的代码就构成了s1的右值引用,并且赋值给了s2
这样就是s1将资源转给了s2,s1 资源无效
本人了解的大概就这么多了
完。