{}在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。
C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,**使用初始化列表时,可添加等号(=),也可不添加。**创建对象时也可以使用列表初始化方式调用构造函数初始化。主要是为了new的时候能够初始化。
| C++98 | C++11 |
|---|---|
int x1 = 1; | int x1 {1};//可以这样写但是不建议 |
int array1[] = { 1, 2, 3, 4, 5 }; | int array1[]{ 1, 2, 3, 4, 5 };//可以这样写但是不建议 |
int array2[5] = { 0 }; | int array2[5]{ 0 };//可以这样写但是不建议 |
struct Point { int _x; int _y;}; //无构造函数 Point* p = new Point[2];//不支持new的时候初始化 | struct Point { int _x; int _y;}; Point* p = new Point[2]{{1,2}, {3,4}}; |
class Point { private:int _x; int _y; Point(int x, int y) :_x(x), _y(y){} }; //有构造函数 Point p(1, 2); | class Point { private:int _x; int _y;}; Point p{ 1, 2 };//可以这样写但是不建议 |
int* p1 = new int[4];//不支持new的时候初始化 | int* p1 = new int[4]{1,1,2,2}; |
std::initializer_list自定义类型用{}去初始化,其实是去匹配对应的构造函数,构造函数支持传几个参数在{}里就只能传几个参数!
而stl库里的容器比如vector、list、deque、forward_list等在使用{}初始化时,可以传入多个参数(不指定个数)。怎么做到的呢?C++11中vector和list的构造函数支持了使用initializer_list初始化。
据我们模拟实现vector的构造函数时可知,没有写那么多个区分参数个数的构造函数。是因为在C++11中把{}单独封装成了std::initializer_list这个类型,类似于vector容器,只不过vector是自己开辟空间存储数据,而{}自己不用存,到常量区找数据就行。【{}跟常量字符串一样,其内容存放在常量区,然后initializer_list这个类型里有2个指针分别指向常量区的开始地址和结束地址。】
vector(std::initializer_list<T> il)
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
reserve(il.size()); //提前开辟空间
auto it = il.begin();
while (it != il.end())
{
push_back(*it);
++it;
}
}
vector<T>& operator=(std::initializer_list<T> l) {
vector<T> tmp(l);
std::swap(_start, tmp._start);
std::swap(_finish, tmp._finish);
std::swap(_endofstorage, tmp._endofstorage);
return *this;
}
用auto修饰的变量必须进行显示初始化,方便让编译器将定义对象的类型设置为初始化值的类型。
declare声明、断言。关键字decltype将变量的类型声明为表达式指定的类型。
typeid(a).name()可以取到变量的类型名,但只能用于输出打印;而typeid(a) ret;不仅可以拿到变量的类型名,还能用来定义一个对象。
由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。
底层就是依靠编译器做了替换,就是迭代器的begin、end和++就能完成。
归个类:
final修饰类,表示不允许被继承,用来修饰虚函数,表示不允许被重写。override修饰虚函数,检查子类虚函数是否完成重写
引用是为了在函数传参和函数传返回值的时候减少拷贝,提高效率。
左值是一个数据的表达式(如变量名或解引用的指针),我们可以获取它的地址,还可以给它赋值。左值可以出现在赋值符号的左边和右边。最重要的区分点是左值可以取地址。比如定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。
const左值引用既可引用左值,也可引用右值。
左值引用解决了函数传参的拷贝问题,且在函数的左值引用参数前加上const,就可以同时接收左值和右值。如 void fun(const int& x);;但并没有解决传返回值的问题,因为一个函数栈帧里面的局部变量出栈帧就销毁了,无法用引用返回,如int& fun(const int& x);==>这就是左值引用尚未解决的问题。想一下杨辉三角vecot<vector<int>>这样的返回值,在传返回值时会有2次拷贝构造,消耗很大,在没有右值引用之前的解决方法之一是用输出型参数带回数据。如果这个临时变量比较小,4字节这样,就会存放在寄存器里;如果临时变量比较大,寄存器放不下,就会压在上一个栈帧的边缘处(这样的话,就算当前栈帧销毁了也不影响数据的保留)。新一点的编译器只需要一次拷贝构造,直接拿着要销毁的栈帧里的返回值去构造接收该值的变量。
右值也是一个数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等。右值不可以取地址。
右值又可以分为纯右值(内置类型表达式的值)和将亡值(自定义类型表达式的值)。
**无论左值引用还是右值引用,都是给对象取别名。**左值可以出现赋值符号的左边,右值只能在赋值符号的右边,不能出现在赋值符号左边。

注意右值是不能取地址的,但是给右值取别名后(即右值引用后),会导致右值被存储到特定位置(比如字面量10本来是在常量区/已初始化数据区,但是会被拷贝一份放到某个区域,比如栈上),且可以取到该位置的地址。虽然局部变量出函数栈帧就销毁了,但是在用右值引用接收函数返回值时,该函数返回值会被存储到特定的地方。
int&& rr1 = 10; &rr1; 不能取字面量10的地址,但是rr1对其进行右值引用后,就可以对rr1取地址,也可以修改rr1;==>移动构造和移动赋值函数可以swap的原因const int rr2&& 10; &rr2;此时可以对字面量10取地址,但不能对rr2做修改。注意!!!右值引用再进行传递,属性就变成左值了!
原来C++类中,有6个默认成员函数:构造函数、析构函数、拷贝构造函数、拷贝赋值重载、取地址重载、const 取地址重载。取地址相对而言没那么重要。
C++11 新增了两个:移动构造函数和移动赋值运算符重载。
如果用户自己写了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。==>说明如果写了移动构造,就得写拷贝构造。
默认生成的移动构造函数/移动赋值运算符重载函数,对于内置类型成员会执行逐成员按字节拷贝(浅拷贝);对于自定义类型成员,则需要看这个成员是否实现移动构造/移动赋值运算符重载函数,如果实现了就调用移动构造/移动赋值运算符重载函数,没有实现就调用拷贝构造/拷贝赋值。
有默认拷贝构造函数、无默认移动构造函数的情况下,如果有右值,就只能走拷贝构造,效率上比较吃亏。
如果用户没有实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载,那么编译器会自动生成一个默认移动构造。
默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝(浅拷贝);对于自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
// 拷贝构造
string(const string& s) :_str(nullptr) {
std::cout << "string(const string& s) -- 深拷贝" << std::endl;
string tmp(s._str);
swap(tmp);
}
// 移动构造
string(string&& s) {
std::cout << "string(string&& s) -- 移动构造" << std::endl;
swap(s);//右值是将亡值,没必要做深拷贝
}
对于传值返回的函数,要传出的值是出了作用域就要销毁掉的,编译器就会把它move成右值,调用移动构造。对于需要深拷贝的自定义类型就不用担心传值返回了
如果用户没有实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载,那么编译器会自动生成一个默认移动赋值。
默认生成的移动赋值运算符重载函数,对于内置类型成员会执行逐成员按字节拷贝(浅拷贝);对于自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)。
// 赋值重载
string& operator=(const string& s) {
std::cout << "string& operator=(string s) -- 深拷贝" << std::endl;
string tmp(s);
swap(tmp);
return *this;
}
// 移动赋值
string& operator=(string&& s)//右值是将亡值{
std::cout << "string& operator=(string&& s) -- 移动语义" << std::endl;
swap(s);//右值是将亡值,没必要做深拷贝
return *this;
}
右值引用单独使用没效果,要搭配移动构造和移动赋值使用,能大大降低拷贝的次数。
右值引用和左值引用减少拷贝的原理不一样,左值引用是取别名,直接起作用;而右值引用是间接起作用,实现移动构造和移动赋值,在构造和赋值的场景下,如果是右值或出作用域就要销毁的左值,编译器可以优化识别为右值,就转移资源。
总结右值引用的好处:右值引用的价值之一:就是补齐这个最后一块短板,传值返回的拷贝问题;右值引用的价值之二:对于插入一些插入右值数据,也可以减少拷贝。
C++11为了让用户更好地控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成,就可以利用该关键字强制生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。
Person(const Person& p) :_name(p._name) ,_age(p._age) {}
Person(Person&& p) = default;//强制生成移动构造
如果能想要限制某些默认函数的生成,在C++98中,做法是将该函数设置成private,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
Person(Person&& p) = delete;//不生成移动构造
通过改造list的代码可知,push_back()增加一个右值引用,就要写两个版本,一个const左值引用版本,一个右值引用版本,才可以实现右值引用。

若某些普通函数不想区分左值和右值,但是传参的时候又有左值和右值混用,那必然要写两个版本的函数,这挺麻烦的。比如下面这个情况
void fun1(const int& x);
void fun1(int&& x);
//int x = 1; fun1(x); --> 调用左值引用版本
//fun1(10); --> 调用右值引用版本
所以在C++11中又扩展了模板的功能,引入万能引用。注意,万能模板只能在未被实例化的时候才能用!
template<class T>
void fun2(T&& t);
这样既能引用左值、const左值,也能引用右值、const右值。
基于将万能引用作为中转站+右值再次传递就会变成左值这个问题引入的解决措施。就是保持右值属性再次传递。
void fun(int& x);
void fun(const int& x);
void fun(int&& x);
void fun(const int&& x);
template<class T>
void fun2(T&& t)
{
fun(std::forward<T>(t));
}
仿函数的出现是为了取代函数指针,想一下当某个对象有很多的属性(int price; int evaluate; string name;等),我们要对每个属性挨个进行排序的时候,就要针对每个属性写一个对应的用于比较大小的仿函数,这就比较麻烦。
lambda表达式实际是一个可调用对象,是没有具体类型的(编译器在实现的时候会加上一串随机字符串确保每个lambda表达式的唯一性<lambda xxxxxxxxx>),用auto即可,其底层就是仿函数。
书写格式为:[capture-list] (parameters) mutable -> return-type { statement
}
[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。必写(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。有就写,没有可以不写mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。一般不需要->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。一般不写,可以自动推导返回类型{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。必写注意:在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数为:[]{}; ,不过该lambda函数不能做任何事情。
auto compare = [](int x, int y) ->bool { return x > y; };//实现比较功能
//后续调用
compare(1, 2);
捕捉列表的写法:传值捕捉、引用捕捉、混合捕捉。
捕捉列表描述了哪些数据可以被lambda使用,以及使用的方式传值还是传引用。注意!捕捉不是传参!
//-------------使用捕捉列表传递参数!这里底层是传值捕捉,只不过用const修饰了,称为复制捕捉
int a = 0, b = 1;
auto compare = [b](int x) ->bool { return x > b; };
compare(a);
//-------------使用捕捉列表传递参数!引用捕捉
int a = 0, b = 1;
auto swap = [&a, &b]() { int tmp = a; a = b; b = tmp; };//这个写法像取地址,实际上是引用捕捉
swap(a, b);
要注意lambda表达式的引用捕捉的写法!
=已经以值传递方式捕捉了所有变量,捕捉a重复;a.让子线程执行从0-100的打印工作
int i = 0;
thread t1([&i]
{
for(; i < 100; ++i) { cout << "thread1: " << i << endl;}
});
b.创建100个线程
vector<thread> vThreads;
int n;
cin >> n;
vThreads.resize(n);
int i = 0;
int num = 0;//标记线程编号
for (auto& t : vThreads)
{
t = thread([&i, num]//这里用的是移动赋值
{
for (; i < 100; ++i)//让每个线程打印1-100
{
cout << "thread: " << num << "->" << i << endl;
}
});
++num;
}
for (auto& t : vThreads)
{
t.join();
}
函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了operator()运算符的类对象。从使用方式上来看,函数对象与lambda表达式完全一样。实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。
修改前
list_node(const T& x) :_next(nullptr) ,_prev(nullptr) ,_data(x) {}//构造函数
void push_back(T x) { insert(end(), x); }
iterator insert(iterator pos, const T& x)
{
node* newnode = new node(x);
node* cur = pos._pnode;
node* prev = cur->_prev;
//链接prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
return iterator(newnode);
}
修改后
list_node(const T& x) :_next(nullptr) ,_prev(nullptr) ,_data(x) {}//构造函数
list_node(T&& x) :_next(nullptr) ,_prev(nullptr) ,_data(move(x)) {}//移动构造//修改属性
void push_back(const T& x) { insert(end(), x); }
void push_back(T&& x) { insert(end(), move(x); }//虽然接收右值,但在该函数内x是左值,所以要move一下
iterator insert(iterator pos, const T& x);
iterator insert(iterator pos, T&& x)
{
node* newnode = new node(move(x);//修改属性
node* cur = pos._pnode;
node* prev = cur->_prev;
//链接prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
return iterator(newnode);
}
注意:用右值参数接收的右值,性质会改变,在该函数作用域内变成左值了【右值引用本身是左值】,想让它保持右值属性,一定要move一下。
修改如下,只需要把强制转换成右值的move替换为std::forward<T>。
list_node(const T& x) :_next(nullptr) ,_prev(nullptr) ,_data(x) {}//构造函数
list_node(T&& x) :_next(nullptr) ,_prev(nullptr) ,_data(std::forward<T>(x)) {}//把强制转右值改成完美转发
void push_back(const T& x) { insert(end(), x); }
void push_back(T&& x) { insert(end(), std::forward<T>(x); }//把强制转右值改成完美转发
iterator insert(iterator pos, const T& x);
iterator insert(iterator pos, T&& x)
{
node* newnode = new node(std::forward<T>(x);//把强制转右值改成完美转发
node* cur = pos._pnode;
node* prev = cur->_prev;
//链接prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
return iterator(newnode);
}
注意:这里不能把左值引用版本的函数删去。因为我们已经实例化了list,模板参数已经确定了,万能引用就起不了作用。
知识基础:1、将拷贝构造函数设置为私有==>防外部调用。存在的缺陷:无法阻止类内某函数内部去访问拷贝构造函数,在运行的时候才报错;2、只声明不实现==>链接找不到,编译出错
C++98做法:只声明为私有,不实现。
C++11做法:加一个禁止生成默认函数的关键字delete。
运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin
我正在运行Ubuntu11.10并像这样安装Ruby1.9:$sudoapt-getinstallruby1.9rubygems一切都运行良好,但ri似乎有空文档。ri告诉我文档是空的,我必须安装它们。我执行此操作是因为我读到它会有所帮助:$rdoc--all--ri现在,当我尝试打开任何文档时:$riArrayNothingknownaboutArray我搜索的其他所有内容都是一样的。 最佳答案 这个呢?apt-getinstallri1.8编辑或者试试这个:(非rvm)geminstallrdocrdoc-datardoc-da
我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat
我目前有一个运行Ruby1.8.7和Rails2.3.2的RubyonRails项目我有一些从数据库中读取数据的单元测试,特别是两个连续项目的日期时间列,这两个项目应该相隔24小时。在一项测试中,我将项目2的日期时间设置为与项目1的日期时间相同。当我执行断言以确保两个值相等时,测试在rails2.3.2下工作正常。当我升级到rails2.3.11时,测试失败显示两次之间的差异将关闭并出现以下错误:expectedbutwas.这两个版本的rails中似乎存在浮点转换问题。如何解决float问题? 最佳答案 这也发生在我身上,我最终这
昨晚看到IDEA官推宣布IntelliJIDEA2023.1正式发布了。简单看了一下,发现这次的新版本包含了许多改进,进一步优化了用户体验,提高了便捷性。至于是否升级最新版本完全是个人意愿,如果觉得新版本没有让自己感兴趣的改进,完全就不用升级,影响不大。软件的版本迭代非常正常,正确看待即可,不持续改进就会慢慢被淘汰!根据官方介绍:IntelliJIDEA2023.1针对新的用户界面进行了大量重构,这些改进都是基于收到的宝贵反馈而实现的。官方还实施了性能增强措施,使得Maven导入更快,并且在打开项目时IDE功能更早地可用。由于后台提交检查,新版本提供了简化的提交流程。IntelliJIDEA
跳过联网激活:OOBE界面直接按Ctrl+Shift+F3进入审核模式。这样就可以直接进入系统进行一些硬件测试等,而不用联网激活导致新机无法退货。需要注意的是,在审核模式下进行的一些操作都会保留,并不会在退出后自动还原!安装的软件在正常开机进系统后还会看见!如果电脑确实没连互联网又不想强行跳过OOBE(网上很多教程会叫你直接结束OOBE进程,但这是不推荐的,因为一些厂商自带优化程序和系统初始化设置在后面都会应用,对于笔记本跳过的话你会发现驱动和内置应用都没有装上。其实这部分脚本就在系统盘的Recovery隐藏文件夹下),可以参考以下方式:https://www.landiannews.com/
在使用Rubyv2.2.2的ElCapitan(MacOSX10.11.1)上安装Rails时,出现以下错误:ERROR:Errorinstallingnokogiri:ERROR:Failedtobuildgemnativeextension./Users/jon/.rvm/rubies/ruby-2.2.2/bin/ruby-r./siteconf20151117-26799-ux15fd.rbextconf.rb--use-system-librariescheckingiftheCcompileraccepts...***extconf.rbfailed***Couldnotc
标题说明了一切。请注意,这不是模型或初始值设定项的更改。我可以删除Controller中的一个实例变量(例如,@user),然后重新加载一个View,它会工作-直到我重新启动服务器,在这种情况下它会提示变量为nil。我正常工作,然后切换到一组完全不同的Controller和View上工作,现在它无缘无故地发生了。应用处于开发环境中。development.rb内容:Dashboard::Application.configuredoconfig.cache_classes=falseconfig.whiny_nils=trueconfig.consider_all_requests_l
rvm通过以下方式正确安装:sudoapt-get安装ruby-rvm当我尝试安装ruby1.9.3时出现这些错误?anthony@SnakeDoc:~$rvminstall1.9.3mkdir:cannotcreatedirectory`/usr/share/ruby-rvm/gems/ruby-1.9.3-p0':PermissiondeniedInstallingRubyfromsourceto:/usr/share/ruby-rvm/rubies/ruby-1.9.3-p0,thismaytakeawhiledependingonyourcpu(s)...ruby-1.
我正在运行全新安装的OSX10.9Mavericks和XCode5.0.1。当我尝试在我的项目中运行bundleinstall时,它无法安装libv8gem。这是输出:ERROR:Errorinstallinglibv8:ERROR:Failedtobuildgemnativeextension./Users/user1/.rvm/rubies/ruby-1.9.3-p448/bin/rubyextconf.rbcreatingMakefileConfiguredwith:--prefix=/Applications/Xcode.app/Contents/Developer/usr--