C++11(15)(上)

新星杯·14天创作挑战营·第18期 10w+人浏览 188人参与

一.统一的列表初始化

        在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 x1 = 1;
    int x2{ 2 };
    int x3 = { 3 };
    int array1[]{ 1, 2, 3, 4, 5 };
    int array2[5]{ 0 };
    Point p{ 1, 2 };
    // C++11中列表初始化也可以适用于new表达式中
    int* pa = new int[4]{ 0 };
}

        创建对象时也可以使用列表初始化方式调用构造函数初始化因此C++11支持多参数的隐式类型转换

class Date
{
public:
Date(int year, int month, int day)
    :_year(year)
    ,_month(month)
    ,_day(day)
{
	cout << "Date(int year, int month, int day)" << endl;
}
private:
int _year;
    int _month;
    int _day;
};
int main()
{
    Date d1(2022, 1, 1); // C++98
    // C++11支持的列表初始化,这里会调用构造函数初始化
    Date d3{ 2022, 1, 2 };
    Date d4 = { 2022, 1, 3 };
    
    return 0;
}

二.声明

        2.1 auto

        在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法

        将其用于实现自动类型判断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。

int main()
{
   int i = 10;
   auto p = &i;
   auto pf = strcpy;

   cout << typeid(p).name() << endl;
   cout << typeid(pf).name() << endl;

   map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };

   //map<string, string>::iterator it = dict.begin();
   auto it = dict.begin();
}

        2.2 decltype

        关键字decltype将变量的类型声明为表达式指定的类型。

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
   decltype(t1 * t2) ret;
   cout << typeid(ret).name() << endl;
}
int main()
{
   const int x = 1;
   double y = 2.2;

   decltype(x * y) ret; // ret的类型是double
   decltype(&x) p;      // p的类型是int*

   cout << typeid(ret).name() << endl;
   cout << typeid(p).name() << endl;
   F(1, 'a');
}

        2.3 nullptr

        由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。

        2.4 delete

        C++11扩展delete的用法,delete除了释放new申请的源外,如果在默认成员函数后跟上=delete,表示让编译器除掉该默认成员函数。\        

        2.5 final和override        

        final:修饰虚函数,表示该虚函数不能再被重写,也同可以修饰类让类不能被继承

        override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报        

        2.6 default

        假设你要使用某个默认函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成默认移动构造了,那么我们可以使用default关键字显示指定默认移动构造生成。 这个移动构造后面会说这里先了解                

        2.7 范围for循环

        之前讲过这里就不进行讲解!

        2.8 智能指针

        后面详细讲解!

        2.9 STL的变化

        关于C++11新容器的变化只需要记得引进了哈希(unordered_map和unordered_set)即可

三.左值引用(&)和右值引用(&&)

        3.1 认识左值引用和右值引用

        传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名

        什么是左值?(可以取地址的值)什么是左值引用?(左值的别名)

        左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边

        定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名

int main()
{
   // 以下的p、b、c、*p都是左值
   int* p = new int(0);
   int b = 1;
   const int c = 2;

   // 以下几个是对上面左值的左值引用
   int*& rp = p;
   int& rb = b;
   const int& rc = c;
   int& pvalue = *p;
}

        

        什么是右值?(不可以取地址的值)什么是右值引用?(右值的别名)

        右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)……

        右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。

int main()
{
   double x = 1.1, y = 2.2;
   // 以下几个都是常见的右值
   10;
   x + y;
   fmin(x, y);

   // 以下几个都是对右值的右值引用
   int&& rr1 = 10;
   double&& rr2 = x + y;
   double&& rr3 = fmin(x, y);

   // 这里编译会报错:error C2106: “=”: 左操作数必须为左值
   10 = 1;
   x + y = 1;
   fmin(x, y) = 1;
}

        需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址。

        此时你可能会有一个疑问:如果右值被取别名后,它是会被存在特定位置,该位置可以取地址,如果我指针指向这里,我对指针修改会影响到该右值吗?

        是的,可以修改,并且确实会影响那个“右值”——但前提是:这个右值是一个可修改的临时对象

int getValue() { return 42; } // 返回一个临时 int 对象

int&& rr = getValue(); // ✅ 合法!rr 绑定到临时对象
int* p = &rr;          // ✅ 可以取地址
*p = 100;              // ✅ 修改成功!临时对象被改变
std::cout << rr;       // 输出 100

        但是如果右值是字面量,它只能被const 右值引用绑定,因此无法对此进行修改

const int&& rr = 10;   
// int* p = &rr;       // ❌ 
// *p = 100;           // 不可以修改

        这个了解一下实际中右值引用的使用场景并不在于此,这个特性也不重要。

        3.2 认识左值引用和右值引用比较

        左值引用总结:   

  1. 左值引用只能引用左值,不能引用右值。

  2. 但是const左值引用既可引用左值,也可引用右值

int main()
{
   // 左值引用只能引用左值,不能引用右值。
   int a = 10;
   int& ra1 = a;   // ra为a的别名
   //int& ra2 = 10;   // 编译失败,因为10是右值

   // const左值引用既可引用左值,也可引用右值。
   const int& ra3 = 10;
   const int& ra4 = a;
   return 0;
}

        右值引用总结:        

  1. 右值引用只能右值,不能引用左值。

  2. 但是右值可以引用move以后的左值。

  3. 右值的分类:内置类型(纯右值)自定义类型(将亡值)

int main()
{
    // 右值引用只能右值,不能引用左值。
    int&& r1 = 10;

    // error C2440: “初始化”: 无法从“int”转换为“int &&”
    // message : 无法将左值绑定到右值引用
    int a = 10;
    int&& r2 = a;

    // 右值引用可以引用move以后的左值
    int&& r3 = std::move(a);
}

        本节课的重点很多,不过最重点的还是左值引用和右值引用,本节课只是带来开胃小菜,下节课将给大家重点分析右值引用为什么出现,它是解决了左值引用的那种问题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值