[C/C++高手进] 函数返回值与返回局部对象的引用!

FengPrince 2013-09-02 09:29:56
主要有2个问题:

大家都知道一条规则:不能返回局部对象的引用。
而且编译器一般也会提示“reference to local variable returned"!

假设定义了类Test:

Test& f()
{
Test t;
t.i=1;
return t;
}

Test g()
{
Test t;
t.i=1;
return t;
}
int main()
{
Test t1=f(); //---(1)
Test t2=g(); //---(2)
}

比较语句(1)和语句(2):
语句(1)在调用f()函数后其栈空间被释放,但数据仍在栈中,而且不会因其它因素而导致数据损坏,因为当前栈帧为main函数的,栈顶指针比返回引用对象t的地址要高!
语句(2)很简单,但实际情况与想像的不一样。

第1个问题
我的理解是:如果返回一个局部对象的引用,程序员能够确保不会对该引用的对象进行修改,只用来读取,那么即使是这种做法不好,也不会导致运行出错。同时,既然是只读应该返回const引用,这样就可以避免去修改引用的局部对象(当然,绕开编译器强行修改该对象除外)!

语句2,我通过汇编发现实际情况有点不同寻常。我不知道,编译器是否都做了优化,我是在LINUX X64 GCC下编译的。汇编代码的结果让我很吃惊:实际调用为:g(&t2);而函数g()变成了如下形式

void g(Test* t)
{
t->i=1;
}

语句2比语句1效率更高!!
PS:从另一方面也可以验证:语句(1)会调用复制构造函数;而语句(2)不会!!

第2个问题
照样正常理解,语句2应该返回一个临时对象;如果返回临时对象,那么临时对象到底存放在栈的哪个位置(寄存器是不可能了,Test类大小大于8Bytes),g()函数栈都已经收回了,临时对象难道放在main函数栈中?

多谢!!
...全文
446 15 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
max_min_ 2013-09-03
  • 打赏
  • 举报
回复 1
数据在栈中可能没有立即擦除内存地址而已! 导致可以拿到有效数据的
赵4老师 2013-09-03
  • 打赏
  • 举报
回复
加static
FengPrince 2013-09-03
  • 打赏
  • 举报
回复
引用 13 楼 akirya 的回复:
[quote=引用 11 楼 FengPrince 的回复:] [quote=引用 9 楼 akirya 的回复:] 编译器可以优化掉一些无关紧要的拷贝构造 gcc直接优化掉了 典型的例子就是 string s = "aaaa";理论上是先执行一次带参数构造,再执行一次拷贝.但实际上就执行一次构造
这个我知道,虽然不需要调复制构造函数,但复制构造函数的语义要求必须得满足,比如不能为private! 我想知道的是,返回值类型时VS会不会把返回的“临时对象”与被调用函数中局部变量当成同一个对象。 就像你举的例了中:test f(){ test x;return x;} t=f();(赋值语句而非初始化) VS会不会像GCC那样,在f()栈中创建x,然后返回时,再在main栈中创建与x相同的临时对象,最后将临时对象复制到main的局部变量t中! 麻烦了!!![/quote] 如果不开优化,那么就是2个对象.开优化就会像gcc一样就是1个对象.[/quote] 十分感谢!! PS:已经结帖了,没有分,望谅解!
  • 打赏
  • 举报
回复
引用 11 楼 FengPrince 的回复:
[quote=引用 9 楼 akirya 的回复:] 编译器可以优化掉一些无关紧要的拷贝构造 gcc直接优化掉了 典型的例子就是 string s = "aaaa";理论上是先执行一次带参数构造,再执行一次拷贝.但实际上就执行一次构造
这个我知道,虽然不需要调复制构造函数,但复制构造函数的语义要求必须得满足,比如不能为private! 我想知道的是,返回值类型时VS会不会把返回的“临时对象”与被调用函数中局部变量当成同一个对象。 就像你举的例了中:test f(){ test x;return x;} t=f();(赋值语句而非初始化) VS会不会像GCC那样,在f()栈中创建x,然后返回时,再在main栈中创建与x相同的临时对象,最后将临时对象复制到main的局部变量t中! 麻烦了!!![/quote] 如果不开优化,那么就是2个对象.开优化就会像gcc一样就是1个对象.
FengPrince 2013-09-03
  • 打赏
  • 举报
回复
引用 11 楼 FengPrince 的回复:
[quote=引用 9 楼 akirya 的回复:] 编译器可以优化掉一些无关紧要的拷贝构造 gcc直接优化掉了 典型的例子就是 string s = "aaaa";理论上是先执行一次带参数构造,再执行一次拷贝.但实际上就执行一次构造
这个我知道,虽然不需要调复制构造函数,但复制构造函数的语义要求必须得满足,比如不能为private! 我想知道的是,返回值类型时VS会不会把返回的“临时对象”与被调用函数中局部变量当成同一个对象。 就像你举的例了中:test f(){ test x;return x;} t=f();(赋值语句而非初始化) VS会不会像GCC那样,在f()栈中创建x,然后返回时,再在main栈中创建与x相同的临时对象,最后将临时对象复制到main的局部变量t中! 麻烦了!!![/quote] VS会不会不像GCC那样
FengPrince 2013-09-03
  • 打赏
  • 举报
回复
引用 9 楼 akirya 的回复:
编译器可以优化掉一些无关紧要的拷贝构造 gcc直接优化掉了 典型的例子就是 string s = "aaaa";理论上是先执行一次带参数构造,再执行一次拷贝.但实际上就执行一次构造
这个我知道,虽然不需要调复制构造函数,但复制构造函数的语义要求必须得满足,比如不能为private! 我想知道的是,返回值类型时VS会不会把返回的“临时对象”与被调用函数中局部变量当成同一个对象。 就像你举的例了中:test f(){ test x;return x;} t=f();(赋值语句而非初始化) VS会不会像GCC那样,在f()栈中创建x,然后返回时,再在main栈中创建与x相同的临时对象,最后将临时对象复制到main的局部变量t中! 麻烦了!!!
FengPrince 2013-09-03
  • 打赏
  • 举报
回复
引用 6 楼 lin5161678 的回复:
[quote=引用 5 楼 FengPrince 的回复:] [quote=引用 1 楼 taodm 的回复:] 语句1既然是错误的,C++标准称之为“未定义行为”,发生啥结果都是正常的。 不要浪费时间在讨论错误的东西有啥“正确”的结果。
好吧,我知道是“未定义行为”。 我其实想问的是在自己完全可控的条件下,能否保证程序的正确性! 这就像char不能保证移植性,因为不同编译器可能实现的有无符号数不一样;但我只要保证使用char的值始终在可ANSI打印字符范围内,而且不用参与算术运算,照样可以跨平台!但很明显,这种做法风险比较大! 其实,有些“未定义行为”是完全可控的,但要自己保证,因为稍有不慎就可能出现纰漏。 我试过了,返回局部对象的引用的情况并非全部可控,因为会调用复制构造函数,进而破坏先前栈的数据,比如保存rdi和rsi寄存器。[/quote]w 你前面的对char的可控 没触犯未定义行为 用这个char 说明不了存在可控的未定义行为 另外不管实现为 有符号 还是无符号 根据你做出的保证 可以保证使用char没有风险 [/quote] 不知道是我没描述清楚还是我理解有误!! 我的意思是控制未定义行为的发生!

char c;
.
.
.
if(c>0X38)
   ...;
是不是可能导致未定义行为?或者准确来讲是Implementation Defined行为! 但如果我能够确保c的范围来保证此语句永远不会发生未定义行为。 同理,返回局部对象的引用,只有当局部对象所在的空间被污染了,才可能导致出错;没污染就不会出错。会不会被污染这一行为是未定义的! 我要做的就是确保该数据不被污染(破坏)! 或许我压根就做不到?? 这才是我想问的,就目前而言,没有人给出我不可控的证据! PS:准备结帖了,这个东西找工作意义不大!!
  • 打赏
  • 举报
回复
编译器可以优化掉一些无关紧要的拷贝构造 gcc直接优化掉了 典型的例子就是 string s = "aaaa";理论上是先执行一次带参数构造,再执行一次拷贝.但实际上就执行一次构造
  • 打赏
  • 举报
回复
引用 7 楼 FengPrince 的回复:
[quote=引用 3 楼 worldy 的回复:] 第一个函数,是返回函数栈中的数据,返回瞬间可以该数据仍然有效,但不保证 第二个函数,编译器会将函数栈中的返回值数据拷贝到返回栈中
第1个问题在于编译器可能会调用自定义的复制构造函数和赋值重载函数导致损坏先前栈中数据;当然,也可能有其它未知的原因(我这不费话吗?) 第2个问题确实是在返回栈中即在main栈中,我通过赋值操作然后汇编发现,在返回栈中的返回的临时变量其实也就是函数g()中的局部变量t。也就是说,g()中的局部变量也放在了main()栈中而非g()栈中,但g()中保存了指向其局部变量的指针! 事实上,如果返回被调用函数的局部变量的值类型,根本就没有所谓的”临时对象”,这个临时对象会被编译器优化成了被调用函数的局部对象!!这一点,我发现很多书都说错了,包括许多国外的书!当然,或许理论上是对的,我目前只有GCC,没试过VS编译器! PS:关于返回值类型会返回”临时对象”这个问题,我很早就持怀疑态度,因为我觉得编译器不一定非要进行多一步的复制操作,尤其是返回的是较大的类对象,这样效率比较低!兄弟如果有VS可以看看VS的处理方式,thx![/quote]
#include<stdio.h>

struct test
{
	~test()
	{
		puts( "~test" );
	}
	int val[10];
};

test f(){ test x;return x;}

int main()
{
	test x = f();
    return 0;
}
VS系列,都是输出2次~test
FengPrince 2013-09-03
  • 打赏
  • 举报
回复
引用 3 楼 worldy 的回复:
第一个函数,是返回函数栈中的数据,返回瞬间可以该数据仍然有效,但不保证 第二个函数,编译器会将函数栈中的返回值数据拷贝到返回栈中
第1个问题在于编译器可能会调用自定义的复制构造函数和赋值重载函数导致损坏先前栈中数据;当然,也可能有其它未知的原因(我这不费话吗?) 第2个问题确实是在返回栈中即在main栈中,我通过赋值操作然后汇编发现,在返回栈中的返回的临时变量其实也就是函数g()中的局部变量t。也就是说,g()中的局部变量也放在了main()栈中而非g()栈中,但g()中保存了指向其局部变量的指针! 事实上,如果返回被调用函数的局部变量的值类型,根本就没有所谓的”临时对象”,这个临时对象会被编译器优化成了被调用函数的局部对象!!这一点,我发现很多书都说错了,包括许多国外的书!当然,或许理论上是对的,我目前只有GCC,没试过VS编译器! PS:关于返回值类型会返回”临时对象”这个问题,我很早就持怀疑态度,因为我觉得编译器不一定非要进行多一步的复制操作,尤其是返回的是较大的类对象,这样效率比较低!兄弟如果有VS可以看看VS的处理方式,thx!
lin5161678 2013-09-03
  • 打赏
  • 举报
回复
引用 5 楼 FengPrince 的回复:
[quote=引用 1 楼 taodm 的回复:] 语句1既然是错误的,C++标准称之为“未定义行为”,发生啥结果都是正常的。 不要浪费时间在讨论错误的东西有啥“正确”的结果。
好吧,我知道是“未定义行为”。 我其实想问的是在自己完全可控的条件下,能否保证程序的正确性! 这就像char不能保证移植性,因为不同编译器可能实现的有无符号数不一样;但我只要保证使用char的值始终在可ANSI打印字符范围内,而且不用参与算术运算,照样可以跨平台!但很明显,这种做法风险比较大! 其实,有些“未定义行为”是完全可控的,但要自己保证,因为稍有不慎就可能出现纰漏。 我试过了,返回局部对象的引用的情况并非全部可控,因为会调用复制构造函数,进而破坏先前栈的数据,比如保存rdi和rsi寄存器。[/quote] 你前面的对char的可控 没触犯未定义行为 用这个char 说明不了存在可控的未定义行为 另外不管实现为 有符号 还是无符号 根据你做出的保证 可以保证使用char没有风险
FengPrince 2013-09-03
  • 打赏
  • 举报
回复
引用 1 楼 taodm 的回复:
语句1既然是错误的,C++标准称之为“未定义行为”,发生啥结果都是正常的。 不要浪费时间在讨论错误的东西有啥“正确”的结果。
好吧,我知道是“未定义行为”。 我其实想问的是在自己完全可控的条件下,能否保证程序的正确性! 这就像char不能保证移植性,因为不同编译器可能实现的有无符号数不一样;但我只要保证使用char的值始终在可ANSI打印字符范围内,而且不用参与算术运算,照样可以跨平台!但很明显,这种做法风险比较大! 其实,有些“未定义行为”是完全可控的,但要自己保证,因为稍有不慎就可能出现纰漏。 我试过了,返回局部对象的引用的情况并非全部可控,因为会调用复制构造函数,进而破坏先前栈的数据,比如保存rdi和rsi寄存器。
worldy 2013-09-02
  • 打赏
  • 举报
回复
第一个函数,是返回函数栈中的数据,返回瞬间可以该数据仍然有效,但不保证 第二个函数,编译器会将函数栈中的返回值数据拷贝到返回栈中
图灵狗 2013-09-02
  • 打赏
  • 举报
回复
++
引用 1 楼 taodm 的回复:
语句1既然是错误的,C++标准称之为“未定义行为”,发生啥结果都是正常的。 不要浪费时间在讨论错误的东西有啥“正确”的结果。
taodm 2013-09-02
  • 打赏
  • 举报
回复
语句1既然是错误的,C++标准称之为“未定义行为”,发生啥结果都是正常的。 不要浪费时间在讨论错误的东西有啥“正确”的结果。
目录 第1篇初级篇 第1章 初识C++ 1.1 c++简介 1.2 C++与C的区别 1.3 学习c++之前需要先学C吗 1.4 c++与其他语言的区别 1.5 c++的版本以及安装问题 第2章 做一个最简短的C4-+程序 2.1 简单的屏幕输出小程序 2.2 输出语句的使用 2.3 std::介绍 2.4 iostream与iostream.h的区别 2.5 重名问题 2.6 注释 2.7 总结 第3章 初步了解函数 3.1 一个简单的函数 3.2 函数的传参 3.3 函数返回、参数与变量 3.4.函数的声明与定义 3.5 局部变量 3.6 全局变量 3.7 总结 第4章 C4-+数据类型 4.1 变量的定义 4.2 将变量及数据存储在内存中 4.3 布尔型变量 4.4 字符型变量 4.5 wchart双字符型变量 4.6 整型概述 4.7 整型变量的定义 4.8 浮点型变量 4.9 常量 4.10枚举型常量 第5章 if语句与运算符 5.1 语句的定义 5.2 块的定义 5.3 表达式的定义 5.4 运算符的定义 5.4.1 赋值运算符的定义 5.4.2 数学运算符的定义 5.4.3 赋值运算符与数学运算符的联合 5.5 自加与自减 5.5.1 前置 5.5.2 后置 5.6 表达式的优先级 5.7 关系运算符 5.8 if语句 5.8.1 else语句 5.8.2 elseif语句 5.8.3 if语句的嵌套 5.9 逻辑运算符及其使用 5.9.1 逻辑“与” 5.9.2 逻辑“或” 5.9.3 逻辑“非” 5.9.4 逻辑运算符的优先级 5.9.5 运算式的真假关系 5.1 0三目运算符 5.1 0.1 三目运算符的优先问题 5.1 0.2 三目运算符的使用问题 5.1 0.3 三目运算符的型别问题 5.1 0.4 三目运算符在字符型变量中的应用 5.1 1复杂嵌套的if语句 5.1 2总结 第6章 面向对象 6.1 面向对象程序语言的主要特征 6.2 类、对象和成员 6.3 类、对象和成员的使用方法及区别 6.3.1 声明一个类 6.3.2 命名习惯 6.3.3 定义一个对象 6.3.4 类与对象的区别 6.3.5 对象与成员的关系 6.3.6 不要给类赋值 6.3.7 对象只能调用类中存在的方法 6.4 公有 6.5 私有 6.6 成员函数的声明和定义 6.7 内联函数 6.7.1 普通内联函数 6.7.2 内联成员函数 6.8 头文件与源文件 6.9 const成员函数 6.10构造函数 6.11默认构造函数 6.12析构函数 6.13析构对象数组 6.14总结 第7章 循环语句 7.1 循环语句的前身——goto语句 7.2 慎用goto语句 7.3 while语句 7.3.1 带运算符的while语句 7.3.2 以字符为条件的while语句 7.3.3 限定while循环的次数 7.3.4 continue语句 7.3.5 break语句 7.3.6 永不休止的while循环 7.4. do……while循环 7.5 for循环 7.5.1 灵活的for循环 7.5.2 条件为空的for循环 7.5.3 执行为空的for循环 7.5.4 嵌套的for循环 7.6 switch语句 7.6.1 switch语句常见错误 7.6.2 switch的菜单功能 7.7 总结 第8章 指针 8.1 什么是地址 8.2 用指针来保存地址 8.2.1 空指针 8.2.2 指针与变量类型 8.2.3 用指针来访问值 8.2.4 指针地址、指针保存的地址和 该地址的值 8.2.5 指针对数值的操作 8.2.6 更换指针保存的地址 8.3 为什么使用指针 8.3.1 栈和堆 8.3.2 用指针创建堆中空间 8.3.3 用指针删除堆中空间 8.4 动态内存 8.4.1 内存泄漏 8.4.2 在堆中创建对象 8.4.3 在堆中删除对象 8.4.4 访问堆中的数据成员 8.4..5 在构造函数中开辟内存空间 8.4.6 对象在栈与堆中的不同 8.5 this指针 8.6 指针的常见错误 8.7 指针运算 8.7.1 指针的加减运算 8.7.2 指针的赋值运算 8.7 _3指针的相减运算 8.7.4 指针的比较运算 8.8 指针 8.8.1 常量指针 8.8.2 指向常量的指针 8.8.3 指向常量的常指针 8.9 总结 第9章 引用 9.1 什么是引用 9.1.1 引用的地址 9.1.2 引用就是别名常量 9.1.3 引用对象 9.1 4空引用 9.2 函数的参数传递 9.2.1 按值传递 9.2.2 按址传递 9.2.3 按别名传递 9.2.4 让函数返回多个值 9.3 传递对象 9.3.1 按值来传递对象 9.3.2 利用指针来传递对象 9.3.3 利用cost指针来传递对象 9.3.4 利用引用来传递对象 9.3.5 到底是使用引用还是指针 9.3.6 引用和指针可以一块用 9.4 引用应注意的问题 9.4.1 引用容易犯的错误 9.4.2 引用一个按值返回的堆中对象 9.4 -3引用一个按别名返回的堆中对象 9.4.4 在哪里创建,就在哪里释放 9.5 总结 第10章 深入函数 10.1 函数重载 10.1.1 普通函数的重载 10.1.2 成员函数的重载 10.2 函数的默认参数 10.3 重载构造函数 10.3.1 成员变量的赋值与初始化 10.3.2 成员变量的初始化与构造函数 10.3.3 复制构造函数 10.3.4 构造函数和new运算符 10.3.5 再谈默认构造函数 10.4.析构函数和delete运算符 10.4..1 默认析构函数 10.4.2 调用构造函数行类型转换 10.5 浅层复制构造函数 10.6 深层复制构造函数 第11章 运算符重载 11.1 运算符重载 11.2 在成员函数中实现自加 11.3 重载前置自加运算符 11.4 创建临时对象 11.5 创建无名临时对象 11.6 取消创建临时对象 11.7 重载后置自加运算符 11.8 重载加法运算符函数operator+ 11.9 重载赋值运算符函数operator 11.10转换类型运算符 11.10.1 温习调用构造函数实现的类型转换 11.10.2 通过构造函数将变量转换为一个对象的成员变量 11.10.3 通过operator关键字行转换 11.11什么可以重载,什么不可以重载 第12章 继承 12.1 什么是继承和派生 12.1.1 复杂的继承和派生 12.1.2 继承和派生如何在C++中实现 12.1.3 继承的种类及语法 12.1.4 单一继承 12.2 公有型、保护型和私有型 12.3 访问权限 12.4 多重继承 12.5 继承的构造与析构 12.6 合理利用基类构造函数 12.7 继承和重载的两义性问题 12.7.1 多重继承容易产生两义性 12.7.2 继承中的重载 12.7.3 两义性的归属问题 12.7.4 减少两义性产生的混淆问题 12.7.5 虚基类不会产生两义性 12.8 总结 第13章 虚函数 13.1 指向子对象的父指针 13.2 虚函数 13.3 拳击游戏 13.4 继承是否可以实现多态性 13.5 在编译时的静态联编 13.6 在运行时的静态联编 13.7 在运行时的动态联编 13.8 在编译时的动态联编 13.9 调用虚函数 13.9.1 在虚函数中调用成员函数 13.9.2 3种调用虚函数的方式比较 13.10被继承的虚函数仍然是虚函数 13.11系统是如何调用虚函数的 13.12在虚函数中使用成员名限定 13.13虚析构函数 13.14总结 第14章 数组 14.1 数组的基本用法 14.1.1 什么是数组 14.1.2数组元素 14.1.3数组下标越界 14.1.4 倒序输出 14.1.5 将数组的下标定义为常量 14.1.6 手动操作数组元素 14.1.7 数组的初始化 14.2 数组的用途 14.2.1 求平均考试成绩 14.2.2 兔子繁殖问题 14.2.3 数字排序问题 14.3 数组在内存中的分布 14.4.输出数组名 14.5 数组名与函数 14.6 传递与接收 14.7 数组与函数 14.7.1 函数传参实例一——求数组所有元素的和 14.7.2 函数传参实例二——用递增法查找数据 14.7.3 函数传参实例三——用二分算法查找数据 14.7.4 函数传参实例四——判断数组是否按照顺序排列 14.7.5 函数传参实例五——判断数组排列方式后执行不同的函数 14.8 数组在对象中的传参 14.9 对象数组 14.10 在对象数组中初始化成员变量 14.11 指针数组 14.12 枚举常量与数组 14.13 多维数组 14.14 多维数组的初始化 14.15 字符数组 14.16 重载数组下标操作符 14.17 总结 第15章 链表 15.1 声明链表结构 15.2 简单的图书链表 15.2.1 图书链表 15.2.2 类的链表 15.3 动态链表 15.3.1 动态链表的建立 15.3.2 解决输入字符造成死循环的问题 15.3.3 动态链表的显示 15.3.4 动态链表的删除 第16章 多态性 第17章 类的特殊成员 第2篇 高级篇 第19章 代码重用 第20篇 高级篇 第20章 友元类与嵌套类 第21章 流 第22章 命名空间 第23章 模板 第24章 异常和错误处理 第25章 补充知识 附录A ASCII码对照表 附录B C++的关键字 附录C C++常用头文件列表 附录D 运算符的优先级 后记
目 录 译者序 前言 第1章 对象的演化 1 1.1 基本概念 1 1.1.1 对象:特性+行为 1 1.1.2 继承:类型关系 1 1.1.3 多态性 2 1.1.4 操作概念:OOP程序像什么 3 1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 复杂性 5 1.3.2 内部原则 6 1.3.3 外部原则 7 1.3.4 对象设计的五个阶段 9 1.3.5 方法承诺什么 10 1.3.6 方法应当提供什么 10 1.4 起草:最小的方法 12 1.4.1 前提 13 1.4.2 高概念 14 1.4.3 论述(treatment) 14 1.4.4 结构化 14 1.4.5 开发 16 1.4.6 重写 17 1.4.7 逻辑 17 1.5 其他方法 17 1.5.1 Booch 18 1.5.2 责任驱动的设计(RDD) 19 1.5.3 对象建模技术(OMT) 19 1.6 为向OOP转变而采取的策略 19 1.6.1 逐步入OOP 19 1.6.2 管理障碍 20 1.7 小结 21 第2章 数据抽象 22 2.1 声明与定义 22 2.2 一个袖珍C库 23 2.3 放在一起:项目创建工具 29 2.4 什么是非正常 29 2.5 基本对象 30 2.6 什么是对象 34 2.7 抽象数据类型 35 2.8 对象细节 35 2.9 头文件形式 36 2.10 嵌套结构 37 2.11 小结 41 2.12 练习 41 第3章 隐藏实现 42 3.1 设置限制 42 3.2 C++的存取控制 42 3.3 友元 44 3.3.1 嵌套友元 45 3.3.2 它是纯的吗 48 3.4 对象布局 48 3.5 类 48 3.5.1 用存取控制来修改stash 50 3.5.2 用存取控制来修改stack 51 3.6 句柄类(handle classes) 51 3.6.1 可见的实现部分 51 3.6.2 减少重复编译 52 3.7 小结 54 3.8 练习 54 第4章 初始化与清除 55 4.1 用构造函数确保初始化 55 4.2 用析构函数确保清除 56 4.3 清除定义块 58 4.3.1 for循环 59 4.3.2 空间分配 60 4.4 含有构造函数和析构函数的stash 61 4.5 含有构造函数和析构函数的stack 63 4.6 集合初始化 65 4.7 缺省构造函数 67 4.8 小结 68 4.9 练习 68 第5章 函数重载与缺省参数 69 5.1 范围分解 69 5.1.1 用返回重载 70 5.1.2 安全类型连接 70 5.2 重载的例子 71 5.3 缺省参数 74 5.4 小结 81 5.5 练习 82 第6章 输入输出流介绍 83 6.1 为什么要用输入输出流 83 6.2 解决输入输出流问题 86 6.2.1 预先了解操作符重载 86 6.2.2 插入符与提取符 87 6.2.3 通常用法 88 6.2.4 面向行的输入 90 6.3 文件输入输出流 91 6.4 输入输出流缓冲 93 6.5 在输入输出流中查找 94 6.6 strstreams 96 6.6.1 为用户分配的存储 96 6.6.2 自动存储分配 98 6.7 输出流格式化 100 6.7.1 内部格式化数据 101 6.7.2 例子 102 6.8 格式化操纵算子 106 6.9 建立操纵算子 108 6.10 输入输出流实例 111 6.10.1 代码生成 111 6.10.2 一个简单的数据记录 117 6.11 小结 123 6.12 练习 123 第7章 常量 124 7.1 值替代 124 7.1.1 头文件里的const 124 7.1.2 const的安全性 125 7.1.3 集合 126 7.1.4 与C语言的区别 126 7.2 指针 127 7.2.1 指向const的指针 127 7.2.2 const指针 127 7.2.3 赋值和类型检查 128 7.3 函数参数和返回 128 7.3.1 传递const值 128 7.3.2 返回const值 129 7.3.3 传递和返回地址 131 7.4 类 133 7.4.1 类里的const和enum 133 7.4.2 编译期间类里的常量 134 7.4.3 const对象和成员函数 136 7.4.4 只读存储能力 139 7.5 可变的(volatile) 140 7.6 小结 141 7.7 练习 141 第8章 内联函数 142 8.1 预处理器的缺陷 142 8.2 内联函数 144 8.2.1 类内部的内联函数 145 8.2.2 存取函数 146 8.3 内联函数和编译器 150 8.3.1 局限性 150 8.3.2 赋值顺序 150 8.3.3 在构造函数和析构函数里隐藏行为 151 8.4 减少混乱 152 8.5 预处理器的特点 153 8.6 改的错误检查 154 8.7 小结 155 8.8 练习 155 第9章 命名控制 157 9.1 来自C语言中的静态成员 157 9.1.1 函数内部的静态变量 157 9.1.2 控制连接 160 9.1.3 其他的存储类型指定符 161 9.2 名字空间 161 9.2.1 产生一个名字空间 162 9.2.2 使用名字空间 163 9.3 C++中的静态成员 166 9.3.1 定义静态数据成员的存储 166 9.3.2 嵌套类和局部类 168 9.3.3 静态成员函数 169 9.4 静态初始化的依赖因素 171 9.5 转换连接指定 174 9.6 小结 174 9.7 练习 174 第10章 引用和拷贝构造函数 176 10.1 C++中的指针 176 10.2 C++中的引用 176 10.2.1 函数中的引用 177 10.2.2 参数传递准则 178 10.3 拷贝构造函数 179 10.3.1 传值方式传递和返回 179 10.3.2 拷贝构造函数 182 10.3.3 缺省拷贝构造函数 187 10.3.4 拷贝构造函数方法的选择 188 10.4 指向成员的指针

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧