您的位置:首页 > 新闻 > 热点要闻 > android开发难吗_装修设计网站有哪些_网络公司seo推广_今日国际重大新闻事件

android开发难吗_装修设计网站有哪些_网络公司seo推广_今日国际重大新闻事件

2025/3/4 20:17:47 来源:https://blog.csdn.net/2303_78095330/article/details/145924009  浏览:    关键词:android开发难吗_装修设计网站有哪些_网络公司seo推广_今日国际重大新闻事件
android开发难吗_装修设计网站有哪些_网络公司seo推广_今日国际重大新闻事件

目录

第一节:多参数的隐式类型转化

第二节:自动推导类型

        2-1.decltype

        2-2.typeid

第三节:其他内容

        3-1.nullptr

         3-2.范围for

        3-3.智能指针

        3-4.STL中的新容器

第四节:右值引用

        4-1.什么是右值

        4-2.右值引用

        4-3.右值引用的意义

                4-3-1.类构造

                4-3-2.访问临时对象内存空间

                4-3-3.延长生命周期

        4-4.总结 

第五节:函数模板的万能引用

        5-1.万能引用

        5-2.完美转发

下期预告:


        以下内容不考虑编译器的优化。

第一节:多参数的隐式类型转化

        C++11允许多参数的隐式类型转化:

class A a = {1,2};

        它实际上先进行了一次构造,创建了一个临时对象,然后使用拷贝构造/移动构造:

                                

        其次,"="可以省略:

class A a{1,2}; // 等价于class A a = {1,2}; 

        内置类型也可以使用{}进行初始化:

int a = {1}; //int a{1};

        上述进行初始化的{}实际上也是一个类 :std::initializer_list<T>,这个类有两个成员指针。一个指向常量数组的第一个元素,一个指向最后一个元素的后一个位置,支持迭代器。

        std::initializer_list<T>可以给多种容器赋值,这是因为它们都有使用std::initializer_list<T>作为参数的构造函数,例如vector:

    _CONSTEXPR20 vector(initializer_list<_Ty> _Ilist, const _Alloc& _Al = _Alloc()): _Mypair(_One_then_variadic_args_t{}, _Al) {_Construct_n(_Convert_size<size_type>(_Ilist.size()), _Ilist.begin(), _Ilist.end());}

        所以std::vector<int> v = {1,2,3};实际上是先使用{1,2,3}构造出了一个匿名的std::vector<int>({1,2,3})最后给 v 进行构造。

        当然也可以直接使用std::initializer_list<T>进行构造,不产生std::vector的匿名对象:

std::vector<int> v({1,2,3});

        隐式类型转化也允许嵌套:

        ​​​​​​​

         

第二节:自动推导类型

        2-1.decltype

        使用这个操作符可以获得变量类型,可以用来创建相同类型的变量,也可以作为模板参数:

#include <iostream>
#include <vector>
using namespace std;
int main()
{int a = 0;decltype(a) b = 1;std::vector<decltype(a)> v;v.push_back(a);v.push_back(b);return 0;
}

        2-2.typeid

        这个操作符可以得到变量的类型名,以字符串的形式返回:

int main()
{int a = 0;std::vector<int> v;printf("%s\n", typeid(a).name());printf("%s\n", typeid(v).name());return 0;
}

  

        

第三节:其他内容

        3-1.nullptr

        C++11引入nullptr代替NULL,因为NULL的字面量是0(int),在函数可以重载的C++中会发生错配的问题:

void Func(void* ptr)
{std::cout << "传入的是指针" << std::endl;
}void Func(int num)
{std::cout << "传入的是数字" << std::endl;
}int main()
{Func(NULL);return 0;
}

  

        nullptr本质上是 (void*)0,类型是指针,就不会发生这种问题:

  

         3-2.范围for

        范围for就是使用容器的迭代器来遍历容器,这里不再赘述

        3-3.智能指针

        因为C++引入了new操作,这就让向堆区申请空间变得方便了很多,但是也经常忘记释放不用的指针,造成内存泄漏问题:

int main()
{if (true){int* ptr = new int(1);// 其他操作// 忘记释放内存了// ptr生命周期结束}// 外面访问不到ptr,也无法释放内存// 内存泄漏了return 0;
}

        为了解决这种问题,C++又引入了智能指针,智能指针实际上是一个类,它是对指针的封装,包含在头文件<memory>中。

        智能指针作为一个类,出作用域时生命周期结束,会调用析构函数,而析构函数中就有对指针的delete操作,释放内存:

int main()
{if (true){std::shared_ptr<int> ptr(new int(1));// 其他操作// 忘记释放内存了// ptr析构,释放内存}return 0;
}

        3-4.STL中的新容器

        主要是unordered_set和unordered_map。

        还有array 静态数组类,与C语言的数组相比,它有着更完备的越界检查功能,但是不能扩容。

第四节:右值引用

        4-1.什么是右值

        简单来区分,右值就是不能被取地址的数据,左值就是可以被取地址的数据。

        右值包括:字面常量、匿名对象、临时对象、表达式返回值。

        

        字面常量:&10;

        匿名对象:&(class A(1,0));

        临时对象:&(std::to_string(123));

        表达式返回值:&(x+y);

        以上操作都是不被允许的。

        不能取地址的原因是右值的生命周期只在当前语句(将亡值),取地址是无意义的。而且它们都具有一定的常性,本身是无法修改的。

        可以把右值理解为只可以读取,不能修改的数据。

        需要注意:左值不一定就是一个值,也可以是一个表达式:

int *p = a;
&(*p); // *p是一个表达式,可以取地址

        4-2.右值引用

        左值引用是使用&给左值取别名,之前一直在使用:

int a = 0;
int& b = a;

        右值引用自然是给右值取别名,它使用&&:

int x = 3,y = 5;
int&& ref = (x+y);

        也可以使用const+左值引用:         

int x = 3,y = 5;
const int& ref = (x+y);

        需要注意的是,右值引用 ref 本身是个左值,因为这样才能转移和修改数据。

        右值引用的不能直接给左值取别名,但是可以给 move 以后的左值取别名:

int main()
{int x = 3, y = 5;int&& ref = move(x);ref = 0;std::cout << x << std::endl;return 0;
}

        此时 ref 是 x 的别名,对它的改变就是对 x 的改变:

  

        4-3.右值引用的意义

                4-3-1.类构造

         右值引用在类的构造的效率上有重大意义,之前构造类的时候,很多情况都会通过匿名对象、临时对象进行拷贝构造,然后匿名/临时对象的出作用域之后,它们的数据就不再使用了。

        那么能不能免去这个拷贝的过程,直接使用匿名/临时对象的数据呢?

        重载一个右值对象为参数的构造函数即可:

class A
{
public:A(A&& t){void* tmp = ptr;ptr = t.ptr;t.ptr = tmp;}void* ptr; // 指向数据的指针
};

        这样当匿名/临时对象管理的数据就由新构造的类管理起来了,流程如下:

        省去了拷贝的过程。

        这种使用右值的构造又叫移动构造

                4-3-2.访问临时对象内存空间

        使用右值引用给右值取别名后,就可以访问右值的内存空间了:

class A
{
public:int a;
};
int main()
{A&& ref = A();ref.a = 2;std::cout << ref.a << std::endl;return 0;
}

  

                4-3-3.延长生命周期

        将亡值的生命周期本来只在当前语句,因为它是无人管理的内存,出语句之后也不会在访问,系统就会在出语句之后自动回收内存空间,生命周期也就结束了。

        但是如果给右值取别名,系统就会判断这块内存是有人管理的,也就不会出语句就回收内存了:

class A
{
public:~A(){std::cout << "A析构函数" << std::endl;}
};
class B
{
public:~B(){std::cout << "B析构函数" << std::endl;}
};
int main()
{A&& ref = A(); // 有别名的类AB();           // 无别名的类Bstd::cout << "出语句了!\n";return 0;
}

        

        可见,B在出语句前就析构了,A在程序结束时才析构

        4-4.总结 

        实际上,有没有名字也是左值和右值的本质差异,左值天生有一个名字,右值天生没有名字,给右值取别名之后,实际上它们就没有区别了,只是名字的类型区别(左值引用、右值引用)。

        左值引用,右值引用就是给类的函数一个区别处理的分类而已,如果参数是左值引用,我就认为你是之后要用的,我就不修改你的数据,而是拷贝你的数据;参数是右值引用,我就认为你之后不再使用了,直接把你的数据拿过来用就行了。 

        4-2说到的 move 本质是给了内存一个右值引用,然后把这个引用保存起来,如果此时有一个类使用 ref 进行构造,就会调用移动构造,管理x的空间;如果使用 x 进行构造,就会调用拷贝构造,自己开辟一块空间进行管理。

第五节:函数模板的万能引用

        5-1.万能引用

        在函数模板的类型前添加 && 表示万能引用,它只能接收左值、右值、const左值、const右值:

template<class>
void Func1(T&& t)
{//...
}

       传入右值时,t就是右值引用;传入左值时,t就是左值引用。

        5-2.完美转发

        上述 t 在传入右值时是右值引用,前面说过右值引用本身是一个左值,所以当另一个函数接收 t 时,t就会被识别成左值,那么左值、右值就无法分开处理了:

void Func2(int& t)
{std::cout << "收到左值引用" << std::endl;
}
void Func2(int&& t)
{std::cout << "收到右值引用" << std::endl;
}template<class T>
void Func1(T&& t)
{Func2(t);
}int main()
{int x = 0;Func1(move(x));Func1(x);return 0;
}

       

        使用完美转发解决这个问题:

template<class T>
void Func1(T&& t)
{Func2(std::forward<T>(t)); // 完美转发
}

     

        也可以使用强转,强调 t 的类型:

template<class T>
void Func1(T&& t)
{Func2((T&&)t);
};

     

下期预告:

        讲完左值、右值,接下来是lambda表达式和C++11引入的类的新功能。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com