出错的原因是编译器根据字符串"hello world"构造一个string类型的临时对象,这个临时变量具有const属性,当这个临时变量传递给非const的string&引用类型时,无法隐式完成const...解决办法是将print()函数的参数改为常引用。代码修改如下,可顺利通过编译。...2.临时变量常量性的原因 为什么临时对象作为引用参数传递时,形参必须是常量引用呢?很多人对此的解释是临时变量是常量,不允许赋值改动,所以作为非常量引用传递时,编译器就会报错。...IntClass(6)表示生成一个无名临时变量并作为左值被修改,所以临时变量并不是常量,只是编译器从语义层面限制了临时变量传递给非const引用。...但如果把一个临时变量当作非const引用参数传进来,由于临时变量的特殊性,临时变量所在的表达式执行结束后,临时变量就会被释放,所以,一般说来, 修改一个临时变量是毫无意义的,据此,C++编译器加入了临时变量不能作为非
int *pRes,在函数体中 new了一块内存并赋值 12,将内存地址赋值给指针 pRes。...在main函数中,定义了指针pInt,调用func函数,把pInt作为参数传入func函数中。结果*pInt并不是 12。...、传引用区别和联系 传值:实参拷贝传递给形参。...无论传值还是传指针,函数都会生成一个临时变量,但传引用时,不会生成临时变量, 传值时,只可以引用值而不可以改变值,但传值引用时,可以改变值, 传指针时,只可以改变指针所指的内容,不可以改变指针本身,但传指针引用时...,既可以改变指针所指的内容,又可以改变指针本身, 引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本
部分传返回值的问题(非局部对象):在函数返回一个临时对象时,如果返回类型是一个对象而不是引用或指针,会导致拷贝构造函数被调用,产生额外的开销。...在返回内置类型时,编译器会进行优化,避免不必要的拷贝操作,直接将返回值传递给调用者或存储在临时变量中 将局部变量作为返回值返回,编译器会创建一个临时变量(临时对象)来存储这个返回值,从而避免返回一个指向已经被销毁内存的引用...拷贝构造函数的目的是将一个对象的值复制到另一个对象中,以确保临时变量拥有正确的值 那这个临时变量存在哪里呢?...通过移动构造函数,可以将一个临时对象(右值引用)的资源(如堆上分配的内存)“移动”给另一个对象,而不是进行昂贵的拷贝操作。...,而不是先创建一个临时对象再拷贝或移动到容器中。
: 0025FA20 0025FA20 5 c 0025F950 在执行输入操作是,实参localStuff是以传引用的方式进入函数operator>>,形参变量w接收的是localStuff的地址,任何对...在随后的抛出异常的操作中,尽管catch子句捕捉的是异常对象的引用,但是捕捉到的异常对象已经不是localStuff,而是它的一个拷贝。...该拷贝构造函数是对象的静态类型(static type)所对应的类的拷贝构造函数,而不是对象的动态类型(dynamic type)对应类的拷贝构造函数。 考察如下程序。...在catch中捕获的是异常对象的引用,所以拷贝构造函数构造的Stuff对象与在catch块中使用的对象w是同一个对象,因为他们具有相同的地址0x0022F738。...这是因为localStuff通过拷贝构造函数传递给异常对象,而异常对象又通过拷贝构造函数传递给catch子句中的对象w。
: 0025FA20 0025FA20 5 c 0025F950 在执行输入操作是,实参localStuff是以传引用的方式进入函数operator>>,形参变量w接收的是localStuff...在随后的抛出异常的操作中,尽管catch子句捕捉的是异常对象的引用,但是捕捉到的异常对象已经不是localStuff,而是它的一个拷贝。...该拷贝构造函数是对象的静态类型(static type)所对应的类的拷贝构造函数,而不是对象的动态类型(dynamic type)对应类的拷贝构造函数。 考察如下程序。...在catch中捕获的是异常对象的引用,所以拷贝构造函数构造的Stuff对象与在catch块中使用的对象w是同一个对象,因为他们具有相同的地址0x0022F738。...这是因为localStuff通过拷贝构造函数传递给异常对象,而异常对象又通过拷贝构造函数传递给catch字句中的对象w。
; 引用做参数 拷贝构造的第二个特性说拷贝构造函数的参数必须是引用,否则会有无穷递归的现象产生,这是因为传值传参本身就是一次拷贝(传值传参是建立一个临时变量,然后将值拷贝给这个临时变量),而拷贝类就需要调用拷贝构造...浅拷贝,对自定义类型调用其自身的赋值重载函数; 日期类赋值重载的实现 赋值重载一般使用引用做参数(这里其实可以使用传值传参,但是传值传参要拷贝临时变量,所以为了提高效率还是使用引用做参数),并用const...在返回值方面也是使用传引用返回,这样也是为了提高效率(毕竟传值返回的话也是需要一次临时变量的拷贝,虽然VS对此做了优化,在传值返回时如果变量较小就使用寄存器返回,但是标准中是有一次临时变量的拷贝)。...就是因为存在隐式的类型转换。这个赋值并不是将a直接赋值给b的,而是根据b的类型产生了一个临时变量,将a的值赋给临时变量,再由临时变量赋值给b。...,连续构造+拷贝构造->优化为直接构造 ** 可以看到这里只调用了一次构造函数,但是根据前面说的隐式类型转换我们可以知道中间有个临时变量的产生,需要先构造这个临时变量,再将这个临时变量拷贝构造aa,但编译器经过优化以后直接成了将
98中的引用进行区分,C++11将该种方式称之为右值引用 1、左值和右值 概念: 左值与右值是C语言中的概念,但C标准并没有给出严格的区分方式 一般认为:左值可放在赋值符号的左边,右值可以放在复制符号的右边...,都有自己独立的空间,而空间中存放内容也都相同,相当于创建了三个内容完全相同的对象,对于空间是一种浪费,程序的效率也会降低,而且临时对象确实作用不是很大 左值引用的短板: 但是当函数返回对象是一个局部变量...,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回 4、移动语义 右值引用的应用: C++11提出了移动语义概念,即:将一个对象中资源移动到另一个对象中的方式,可以有效缓解该问题...C++11中,std::move()函数位于头文件中,该函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义 move的定义: template...,将参数传递给函数模板中调用的另外一个函数 示例: void Func(int x) { // ...... } template void PerfectForward(
在main函数中,将变量x传递给increment函数后,x的值被递增为11。因为参数是引用类型,所以对i的修改会直接影响到x。...在main函数中,将变量x传递给print函数后,print函数无法修改x的值。这样做可以确保函数不会意外地修改传递给它的参数。...= d;确可以,是因为类型转换会生成临时变量,类型转换是将一个数据类型的值转换为另一个数据类型的值,而不是直接修改原始值。...表达式计算:在进行表达式计算时,如果表达式中包含临时变量的创建和销毁,编译器会在需要的地方生成临时变量。 函数调用:当调用函数时,会将实参传递给形参。...五、传值、传引用效率比较 以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的
1.1 为什么需要拷贝构造函数? 在我们实际的练习或者是做开发的过程中,拷贝操作时必不可少的。我们有内置类型的拷贝操作,亦有自定义类型的操作!...1.2 拷贝构造函数的"概念"(内含语法) 拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般用const修饰),再用已存在的类类型对象创建新对象时由编译器自动调用。...首先带着大家看看,如果拷贝构造函数的形参不是类类型对象的引用会怎样? 编译器报了一个这样的错误:“Date”: 非法的复制构造函数: 第一个参数不应是“Date” 。...函数调用过程中将实参值传递给形参的过程中最能体现这一观点,这也就是为什么我们经常说"值传递时,形参是实参的一份临时拷贝"!。 对于自定义类型的值拷贝来说,编译器会去调用对应的拷贝构造函数!...既然第一个行不通,那我们就用传址调用,然而在绝大场景中C++更喜欢用"引用"来代替指针,因此我们就能完全理解为什么拷贝构造函数的形参类型一定得是类类型的引用了。
例如匿名对象,传值返回的函数调用的返回值等,因为匿名对象在其所在代码行执行完毕后就会被销毁,并且传值返回的函数调用实际利用了中间生成的一个临时变量将返回值从被调用的函数栈帧即将销毁时带出,这个临时变量的值一旦被接收...实际上在以前没有右值引用的时候,是通过输出型参数来解决传值返回代价太大的问题,即在调用函数之前创建好要返回的对象,然后将这个返回的对象通过传引用的方式来传参,在函数内部通过改变这个输出型参数的方式来改变函数外面提前创建好的对象或变量...但如果是右值的拷贝或赋值呢?因为拷贝构造和拷贝赋值的参数是const左值引用,自然可以接收右值的传参,在函数内部必然还是要进行资源的重新拷贝,那是不是有点太浪费了啊?...,他们还是比较正常的,对于左边场景下,也就是先构造临时变量,再拷贝构造ret,构造+拷贝构造会直接优化为构造ret,一般编译器都会调用一次拷贝构造,但我的编译器没有调,没调就没调吧,也不影响我学知识嘛。...对于右边的场景来说,编译器是不能优化的,因为被赋值对象是已经存在的,编译器不能直接构造ret,必须以赋值重载的方式来拷贝ret,那就必须需要一个临时变量将str的资源保存下来,然后将临时变量赋值给ret
拷贝构造函数 拷贝构造函数时一个特殊的构造函数,特殊到我们可以单独把它分为一类,但是它的核心功能还是给一个刚刚开辟好的对象进行初始化,只是它和普通的构造函数不同,它的第一个参数必须是当前类类型对象的引用... 上面就是拷贝构造的基本知识,我们现在来更加深入研究一下拷贝构造函数还会在哪里被用到,以及怎么用的,同时探讨一下为什么第一个参数必须是当前类类型对象的引用,不加引用又会发生什么? ...st,这里会直接将st1拷贝构造一个临时对象出来当作形参,在这个函数中对这个临时对象的修改就是对形参st的修改,可以这样说,这个拷贝构造出来的临时对象就是我们的形参,这是上面代码中的第一次拷贝构造... 随后我们来解决最后一个关于拷贝构造函数的问题,就是拷⻉构造函数的第⼀个参数必须是当前类类型对象的引用,不能使用传值⽅式传参,否则编译器会直接报错,这又是为什么呢? ...这主要还是因为一个规定:类对象传值传参必须调用拷贝构造对形参进行初始化,那么如果我们的拷贝构造函数没有加引用,这里的拷贝构造就属于一个传值传参的函数,在我们给拷贝构造函数传参时我们需要调用拷贝构造,随后我们又去调用拷贝构造
右值可以用于初始化一个变量、传递给函数或者返回给调用者。 右值具有如下特点: 右值可以是字面量、临时对象、表达式的结果或者一些函数的返回值。 右值没有持久性,它们不能出现在赋值运算符的左侧。...例如:将整型转换为string类型的函数:string to_string(int value)函数中可以看到,这里只能使用传值返回,传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造...因为右值一般是临时对象,都是将亡值,所有我们可以在bit::string中增加移动构造和移动赋值,对右值对象使用,将参数右值的资源窃取过来,占位已有,那么就不用进行深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己...string类型的函数:string to_string(int value)函数中传值返回构造的临时对象是右值,很快就会被销毁,所以将它拷贝给str时我们就可以通过移动构造直接交换临时对象的内容:...这里可以看到传值返回时需要构造两次,但是编译器一般会给出优化,将临时对象的拷贝省去,只构造一次: 我们会发现,这里没有调用深拷贝的拷贝构造,而是调用了移动构造,移动构造中没有新开空间,拷贝数据,所以效率提高了
析构函数: 与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。...在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?...拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。...而我们都知道传值传参,形参是实参的一份临时拷贝! 那么拷贝构造函数如果不是引用就会形成无穷递归调用。有人说为什么不写一个返回条件来结束递归?...则int&接收double d是不行的,需要const引用,ri为临时变量的别名。
正常对一个自定义类传值返回是需要进行3次构造的,函数体内将构造需要返回 str 对象,在返回 str 时先对其拷贝构造出一个临时对象 tmp ,函数体外的用于接收返回值的 ret 再去拷贝构造这个 tmp...如下图: 聪明的编译器设计师一想,这样不慢了啊,干脆不构建临时对象,直接将 str 拷贝构造给 ret 不就行了。...增加移动构造和移动赋值的传值返回 移动构造和移动赋值本质上就是掠夺资源,即使在函数体内的 str 对象是左值,但是它是临时对象,出了作用域就销毁了,所以可以将其视作特殊的右值,这里隐式地调用 move...有人可能会疑惑,为什么右值引用的函数能传入左值a? 因为这里的 && 其实不代表右值引用,当你传左值时,函数会将其识别成左值的引用 T& ,然后触发引用折叠,成为一个左值引用。...,确保传递给下一层的函数的参数保持器原有的属性(左值或者右值)。
因为const 变量或者引用都得在定义的时候初始化,所以const 成员和引用成员必须在初始化列表中初始化。另外,可以使用定义枚举类型来得到类作用域共有的常量。...这时要在内存新建立一个局部对象,并把实参拷贝到新的对象中。理所当然也调用拷贝构造函数。还有一点,为什么拷贝构造函数的参数需要是引用? ...这是因为如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数...所谓return 对象,实际上是调用拷贝构造函数把该对象的值拷入临时对象。如果返回的是变量,处理过程类似,只是不调用构造函数。...将TestFun(t); 换成 Test t2 = TestFun3(t); ? 函数返回调用拷贝构造函数,但没有再次调用拷贝构造函数,而且没有释放临时对象,可以理解成临时对象改名为t2 了。
将临时对象资源转移 对于 to_string() 函数来说,也不应该只发生一次 移动构造,实际应该先把 ret 拷贝给 临时对象,再将 临时对象 中的资源转移;但编译器判断 ret 是一个局部变量,出了函数就销毁了...答案是 不行,不是说单纯的 右值引用 解决了 无效深拷贝 问题,而是基于 右值引用 实现的 移动构造 解决了问题,所以无论是 右值引用 还是 左值引用,在面对 传值返回时,都不能作为函数返回值类型,返回局部对象引用会导致程序异常退出...>,只要 vector 中实现了 移动构造 函数,就可以避免深拷贝,轻松返回结果 1.5.右值引用的意义 右值引用 是个好东西,它的核心功能在于再次利用 临时资源,避免无意义且低效的拷贝行为...,将临时资源等将亡值的资源通过 移动构造 进行转移,减少拷贝 2.完美转发 泛型编程 是 C++ 中的核心功能之一,典型的让程序员少走弯路,让编译器多干活,伴随着 右值引用 的新概念加入,泛型编程 也需要随之升级...当然不是,模板 是根据我们传入的参数类型,来推导出相应的函数,如果说 模板 推导没有问题,那问题就出在 回调函数 的参数上了,只有推导后,无论传的 左值 还是 右值,编译器都会把 val 变为 左值,这样才能解释为什么最终结果全部为
这其实就在向我们传达了一个信息:初始化过程是在进入构造函数之前完成的!! 所以构造函数的本质并不是初始化成员变量,而是给成员变量赋值!!...(因为给了默认值的参数可以不传递) 但是这样的可读性不是很好,如果我们用explicit修饰构造函数,就可以禁止隐性类型转化 注意: 1、类型转化产生的临时变量具有常性。...如上图,先创建一个Date类的临时对象,但是我们的引用d2并不能成为这个临时变量的别名,因为临时变量有常性!让d2共用会造成权限放大!!...总结:对于传值传参来说,如果构造和拷贝构造在同一行,一般都可以被优化成直接构造,如果跨行了就没办法优化,对于传引用来说,就不涉及到优化(不存在拷贝构造,所以也就不需要优化)。...4、使用建议 函数传参: 1、尽量使用const&传参 2、能用引用传参尽量引用传参(基本上都可以,这样避免了拷贝形参的损耗) 对象返回总结: 1、接受返回值对象时,尽量用拷贝构造的方式接受,不要用赋值接受
1.静态成员函数和静态成员变量的引入 (1)我们通过以下面的这个例子逐步引出静态的成员变量和成员函数: 我们自己定义一个类,使用这个类创建对象,我们应该如何判断在这个程序执行的过程中,创建了多少个对象,...因为我们调用了func这个函数,a1这个实参传递给aa这个形参执行拷贝构造函数,这个时候需要创建对象,而且返回的时候,因为是传值返回,我们需要创建一个对象作为临时变量,所以又要创建一个对象,综上所述,一共是创建了...5个对象; (8)如果在func函数里面,我们是传引用返回,这个时候就不会生成这个临时的对象,这个时候的n就是4;如果我们使用传引用返回而且形参也是引用的,这个时候就不会执行拷贝构造函数,这个时候n=3...;当然因为这个地方的aa这个返回值是出了作用域就会销毁的,并不符合传引用返回的条件,我们在这里只是说明问题,传引用返回实际上是不规范的; 这个过程我们好像并没有使用到静态的成员变量和成员函数,下面我们们将在这个题目的基础上面引入...,属于整个类了,而不是某一个单独的对象,这个时候我们会发现在等号的位置有报错; 这个就说明静态成员变量就不可以给缺省值了,为什么会这样?
复制构造函数具有一般构造函数的所有特性——它的形参是本类的一个对象的引用,作用是用一个已经存在的对象(即为函数的参数)来初始化一个新的对象。...前面我们已经向大家介绍了函数具有 引用传递 的传参方式——我们可以看到,复制构造函数使用的就是引用传参。 为什么这里要使用引用来传参呢?...同样,对于在函数中创建的对象,也是如此——例子中的return a;返回的并不是a这个对象本身,而是通过复制构造函数,在主调函数中用a重新构造的对象。...在函数调用返回的时候,原来的临时对象a的使命已经完成,随着整个函数中的其他临时变量一起被销毁了. Question????...就算是不自己定义复制构造函数,编译器也可以自动帮我们生成一个隐含构造函数——而我们上面的示例中写的复制构造函数,功能跟隐含的复制构造函数其实并没有什么区别。
领取专属 10元无门槛券
手把手带您无忧上云