C++11读书笔记—4(右值系统引论,老C++的若干大坑)
2016-07-31 19:45
190 查看
说右值系统,要从 把C/C++程序员坑了无数遍的“浅拷贝深拷贝”问题说起。之后介绍C++11标准之前的大坑们。
如深拷贝浅拷贝;函数返回对象究竟是什么过程;以及函数的引用返回
struct Person
{
char* pname; //问题在这里
int age;
char sex;
};
如果这样拷贝时,
p1=p2;
memcpy(to,from,sizeof(Person) );
两个结构体对象元素的不同指针同时指向了一个地方。(浅拷贝)
编译器的等号只能将变量的内存空间整体拷贝,如果一个变量a是指针指向这个空间外部。拷贝后对象的变量a也指向这块外部空间。相当于两个对象同时对一个地方宣称主权。
所以这种情况要自己写函数,自己分配处理内存空间。C++类也存在同样的问题。
停,这里会出现令人撕逼的问题答案。
下面分别是我用gcc编译的结果,与VS2015的结果。
咦?这不挺对的吗?怎么会产生撕逼呢?因为老C++标准可不是这么玩的。
因为当函数返回对象的时候。编译器会生成一个临时对象返回,如声明一个函数用来合并两个字符串:
const string strMerge (const string s1, const string s2);
大多时候是无法避免这样的临时变量产生的,但是现代编译器可以将这样的临时变量进行优化掉,这样的优化策略中,有个所谓的“返回值优化”。这是编译器替我们做的。
C++中原标准是这样的:当函数需要返回一个对象的时候,如果自己创建一个临时对象用户返回,那么这个临时对象会消耗一个构造函数(Constructor)的调用、一个复制构造函数的调用(Copy
Constructor)以及一个析构函数(Destructor)的调用的代价。如果按这个标准做,第一个直接赋值结果应该是这样。
直接赋值:
构造函数:1 //函数里面的临时变量
copy构造函数:1
析构函数:1
//返回过程结束//下面执行a=getA();
copy构造函数:2
析构函数:2 //这个是析构那个临时变量
析构函数:3 // 程序结束,析构a。
但是编译器实在看不过去了。编译器说:如果稍微做一点优化,就可以将成本降低到一个构造函数的代价。结果“良心编译器”替我们做了上面运行的结果。
但这么一改就坏了!!!!!!
如果没有良心编译器,因为C++标准在这里用了copy构造函数,再加上深拷贝浅拷贝的问题,我们要想直接赋值,直接返回那么做必须重载copy构造函数。。。。。那我怎么知道编译器是不是良心???于是很多程序员甘愿用第二种指针方式,冒着内存泄漏的风险也不用上一种。
这个区分得回到原先C语言的问题中。就是有些能放等号左边,我们叫左值;有些只能放等号右边,我们叫右值。请注意,我前者没说“仅能放左边”。
书面上讲,左值具名,对应指定内存域,可访问;右值不具名,不对应内存域,不可访问。临时对像是右值。左值可处于等号左边,右值只能放在等号右边。区分表达式的左右值属性有一个简便方法:若可对表达式用 & 符取址,则为左值,否则为右值。
C++有一个特色的东西,函数可以做左值。这个很重要,在c++0x中提出C++11流行的右值引用打了基础。
那么函数的引用究竟返回什么呢?是一个对象。。。不是值
getA() = 100;在这里的意思就是a =100;
getA()放在那里,你就把他看作a(getA()函数内部return的那个东西)就行。
自此C++的右值系统导引的坑都提出来了,我们开始右值系统吧
如深拷贝浅拷贝;函数返回对象究竟是什么过程;以及函数的引用返回
一、浅拷贝与深拷贝
还是简单说一下,这个问题从C语言时代就开始了。struct Person
{
char* pname; //问题在这里
int age;
char sex;
};
如果这样拷贝时,
p1=p2;
memcpy(to,from,sizeof(Person) );
两个结构体对象元素的不同指针同时指向了一个地方。(浅拷贝)
编译器的等号只能将变量的内存空间整体拷贝,如果一个变量a是指针指向这个空间外部。拷贝后对象的变量a也指向这块外部空间。相当于两个对象同时对一个地方宣称主权。
所以这种情况要自己写函数,自己分配处理内存空间。C++类也存在同样的问题。
二、函数返回过程,又一个令人撕逼的问题
先看下面的代码:#include<iostream> using namespace std; class A { public: A():ap(new int(1)) { cout << "构造函数:" << ++n_cons << endl; }; A(const A & olda):ap(new int(*(olda.ap))) { cout << "copy构造函数:" << ++n_copycons << endl; }; ~A() { //delete a;//这个暂时不写没有 cout << "析构函数:" << ++n_des << endl; } public: int* ap; static int n_cons; static int n_copycons; static int n_des; }; A getA() //工厂模式常见操作 { return A(); } //因为坑太大,很多程序员直接不用上面这种容易理解的写法。 //羡慕java程序员吧,常用以下几种方法。 // A* getpA() { A* a = new A(); return a; } int A::n_cons = 0; int A::n_copycons = 0; int A::n_des = 0; int main() { { cout << "直接赋值:" << endl; A::n_cons = 0; A::n_copycons = 0; A::n_des = 0; A a = getA(); } { cout << "用指针:" << endl; A::n_cons = 0; A::n_copycons = 0; A::n_des = 0; A* pa = getpA(); } return 0; }
停,这里会出现令人撕逼的问题答案。
下面分别是我用gcc编译的结果,与VS2015的结果。
咦?这不挺对的吗?怎么会产生撕逼呢?因为老C++标准可不是这么玩的。
因为当函数返回对象的时候。编译器会生成一个临时对象返回,如声明一个函数用来合并两个字符串:
const string strMerge (const string s1, const string s2);
大多时候是无法避免这样的临时变量产生的,但是现代编译器可以将这样的临时变量进行优化掉,这样的优化策略中,有个所谓的“返回值优化”。这是编译器替我们做的。
C++中原标准是这样的:当函数需要返回一个对象的时候,如果自己创建一个临时对象用户返回,那么这个临时对象会消耗一个构造函数(Constructor)的调用、一个复制构造函数的调用(Copy
Constructor)以及一个析构函数(Destructor)的调用的代价。如果按这个标准做,第一个直接赋值结果应该是这样。
直接赋值:
构造函数:1 //函数里面的临时变量
copy构造函数:1
析构函数:1
//返回过程结束//下面执行a=getA();
copy构造函数:2
析构函数:2 //这个是析构那个临时变量
析构函数:3 // 程序结束,析构a。
但是编译器实在看不过去了。编译器说:如果稍微做一点优化,就可以将成本降低到一个构造函数的代价。结果“良心编译器”替我们做了上面运行的结果。
但这么一改就坏了!!!!!!
如果没有良心编译器,因为C++标准在这里用了copy构造函数,再加上深拷贝浅拷贝的问题,我们要想直接赋值,直接返回那么做必须重载copy构造函数。。。。。那我怎么知道编译器是不是良心???于是很多程序员甘愿用第二种指针方式,冒着内存泄漏的风险也不用上一种。
三、左值与右值
(本部分参考这篇文章C++ 0x 之左值与右值、右值引用、移动语义、传导模板)这个区分得回到原先C语言的问题中。就是有些能放等号左边,我们叫左值;有些只能放等号右边,我们叫右值。请注意,我前者没说“仅能放左边”。
书面上讲,左值具名,对应指定内存域,可访问;右值不具名,不对应内存域,不可访问。临时对像是右值。左值可处于等号左边,右值只能放在等号右边。区分表达式的左右值属性有一个简便方法:若可对表达式用 & 符取址,则为左值,否则为右值。
int a,b; a = 3; b = 4; a = b; b = a; // 以下写法不合法。 3 = a; a+b = 4;
C++有一个特色的东西,函数可以做左值。这个很重要,在c++0x中提出C++11流行的右值引用打了基础。
#include<iostream> using namespace std; int & getA() { static int a = 0; //声明,这是初始化,静态就一次。 return a; } int main() { getA() = 100;//函数居然当左值 cout << getA(); return 0; }
那么函数的引用究竟返回什么呢?是一个对象。。。不是值
getA() = 100;在这里的意思就是a =100;
getA()放在那里,你就把他看作a(getA()函数内部return的那个东西)就行。
自此C++的右值系统导引的坑都提出来了,我们开始右值系统吧
相关文章推荐
- C++11读书笔记—5(右值系统)
- C/C++里边如何获得系统时间 【转载】
- C++中建立对象间消息连接的一种系统方法
- C++中建立对象间消息连接的一种系统方法
- C++学生成绩管理系统
- C++ 学生成绩系统代码
- C/C++中空指针与0与NULL和其他若干问题小结。(转帖)
- 超级强的C++大学学籍管理系统
- C++内存分配问题若干_1
- 推荐:给C++程序员的礼物:C#(系统展现了C++与C#的异同点)
- c++中的若干名词__c++学习一
- VB开发MIS系统的若干思考
- 对于一个嵌入式系统,怎么用C++让系统转起来。
- 如何把SEH类型的系统异常转化为C++类型的异常
- C++ 判断64位和32位系统
- 《程序员》11期文章 C++在嵌入式系统中的运用
- C++读取系统时间
- C++中建立对象间消息连接的系统方法
- C/C++中函数设计的若干问题
- [论文]基于C++的虚拟机构造技术及其编译系统的研究与设计 (小型篇)