逐成员按字节拷贝就是浅拷贝。一个类中,如果达成默认移动构造的要求,那么传右值就会使用移动构造了,传左值还是拷贝构造。
强制生成默认函数。比如自己写了拷贝构造,那么移动构造就不会默认生成,那么就可以用default来强制生成。
Person (Person&& p) = default;//移动构造
Person& operator=(Person&& p) = default;//移动赋值
//原本括号里都是const Person& p
但有些编译器会有别的问题,比如用默认的拷贝构造和赋值,再强制生成默认的移动可能就无法生成,要不都用强制生成,要不自己写拷贝构造和赋值。
delete则是禁止生成默认函数。也是= delete写法。
之前学过的final和override,final会让类不能被继承,成员函数不能被重写;override检查派生类的虚函数是否完成重写。
template <class ...Args>
void show(Args... args)
{
;
}
三个点就代表可变参数,Args是一个模板参数包,args是一个函数形参参数包,声明一个参数包Args… args,这个参数包中可以包含0到任意个模板参数。传的时候可以传各种类型的参数。比如传一个字符和一个整数,C++11中这里的可变参数也就是类型不同的参数了。
但在函数中的写法也会有所变化
template <class ...Args>
void show(Args... args)
{
cout << sizeof...(args) << endl;//对于三个点,编译器会自己判断,生成多少个模板参数
}
int main()
{
show();
show('x');
show('x', 'y');
show('x', 1);
return 0l;
}
如何解析可变参数包?typeid不支持使用,不能这样写typeid…(args).name()。
void show()
{
cout << endl;
}
template <class T, class ...Args>
void show(const T& val, Args... args)
{
//cout << sizeof...(args) << endl;
cout << val << " ";
show(args...);
}
int main()
{
show();
show('x');
show('x', 'y');
show('x', 1);
show('x', 1, string("abcd"));
return 0l;
}
这里用的递归思维,第一个show调用没有参数的show,所以直接打印空;第二个就是调用带参数的show,字符x传给val,而参数包此时就是0个参数。第三个开始就是多个参数传过去,val是字符x,参数包是字符y,第一次打印完val,然后再次调用show,此时传过去args…,那么val就是y,参数包是0个参数。
可以看调用了多少次。
void show()
{
cout << endl;
}
template <class T, class ...Args>
void show(const T& val, Args... args)
{
//cout << sizeof...(args) << endl;
cout << __FUNCTION__ << "(" << sizeof...(args) << ")" << endl;
cout << val << " ";
show(args...);
}
int main()
{
//show();
//show('x');
//show('x', 'y');
//show('x', 1);
show('x', 1, string("abcd"));
return 0l;
}
C++的线程会用到这个可变参数包。
还有这样的写法
template<class T>
void PrintArg(T t)
{
cout << t << " ";
}
template<class ...Args>
void show(Args... args)
{
int arr[] = { (PrintArg(args), 0)... };
cout << endl;
}
int main()
{
show(1, 'A', string("abcd"));
return 0;
}
后面的三个点是必须要写的形式,用了逗号表达式,逗号表达式取右边的,因为要初始化这int数组,所以这个0,变成什么数字都可以,如果不加这个逗号表达式,直接写int arr[] = {PrintArg(args)… } 然后在PrintArg函数里写上return 0即可。把三个点放在括号外面可以一个个传过去,具体传多少,编译器知道有多少个模板参数;如果是放在括号里args…,那就是全部传了过去,那就不对了。
在编译器里就变成这样。
void show(char a1, char a2, sting a3)
{
int arr[] = {PrintArg(a1), PrintArg(a2), PrintArg(a3)};
cout << endl;
}
可变参数包让编译器的负担加重了。
实际写的时候更倾向于Args&&… args,在模板这样的引用就是万能引用,也就是引用折叠,因为传自定义类型的时候,像string,就会是传值引用
库中的类的插入部分添加了emplace_back这样的函数,它就用了可变参数包,在普通插入方面push_back其实没有太多区别,用法也都一样,但在传入临时变量会有一定区别,比如在list中emplace_back(“asdasda”),这时候push_back就是先构造匿名对象再传参到右值版本,然后移动构造转移资源;而emplace_back则是直接构造,它会推导成char*类型,然后往下传给要添加的节点,最后构造。
深拷贝的类差别不大,移动构造有一定差距,浅拷贝时,像日期类,如果传右值,差距不大,但是传左值。emplace不能接收初始化的列表,比如{2023, 8, 26},但是可以emplace_back(Date(2023, 8, 26))和emplace_back(2023, 8, 26)是可以的,这两个就是构造+移动构造和直接构造。
本篇gitee
结束。