11.}
12.int&put(intn)
13.{
14.if(n>=0&&n<=9)returnvals[n];
15.else{cout<<"subscripterror";returnerror;}
16.}
复制代码
(5)在另外的一些操作符中,却千万不能返回引用:
+-*/四则运算符。
它们不能返回引用,EffectiveC++[1]的Item23详细的讨论了这个问题。
主要原因是这四个操作符没有sideeffect,因此,它们必须构造一个对象作为返回值,可选的方案包括:
返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。
根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。
静态对象的引用又因为((a+b)==(c+d))会永远为true而导致错误。
所以可选的只剩下返回一个对象了。
6.“引用”与多态的关系?
引用是除指针外另一个可以产生多态效果的手段。
这意味着,一个基类的引用可以指向它的派生类实例。
例4
1.ClassA;
2.ClassB:
ClassA{...};
3.Bb;
4.A&ref=b;
复制代码
7.“引用”与指针的区别是什么?
指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。
程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
此外,就是上面提到的对函数传ref和pointer的区别。
8.什么时候需要“引用”?
流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。
以上2-8参考:
9.结构与联合有何区别?
1.结构和联合都是由多个不同的数据类型成员组成,但在任何同一时刻,联合中只存放了一个被选中的成员(所有成员共用一块地址空间),而结构的所有成员都存在(不同成员的存放地址不同)。
2.对于联合的不同成员赋值,将会对其它成员重写, 原来成员的值就不存在了,而对于结构的不同成员赋值是互不影响的。
10.下面关于“联合”的题目的输出?
a)
1.#include
2.union
3.{
4.inti;
5.charx[2];
6.}a;
7.voidmain()
8.{
9.a.x[0]=10;
10.a.x[1]=1;
11.printf("%d",a.i);
12.}
复制代码
答案:
266(低位低地址,高位高地址,内存占用情况是Ox010A)
b)
1.main()
2.{
3. union{ /*定义一个联合*/
4. inti;
5. struct{ /*在联合中定义一个结构*/
6. charfirst;
7. charsecond;
8. }half;
9. }number;
10. number.i=0x4241; /*联合成员赋值*/
11. printf("%c%c\n",number.half.first,mumber.half.second);
12. number.half.first='a'; /*联合中结构成员赋值*/
13. number.half.second='b';
14. printf("%x\n",number.i);
15. getch();
16.}
复制代码
答案:
AB (0x41对应'A',是低位;Ox42对应'B',是高位)
6261(number.i和number.half共用一块地址空间)
11.已知strcpy的函数原型:
char*strcpy(char*strDest,constchar*strSrc)其中strDest是目的字符串,strSrc是源字符串。
不调用C++/C的字符串库函数,请编写函数strcpy.
答案:
1.char*strcpy(char*strDest,constchar*strSrc)
2.{
3.if(strDest==NULL||strSrc==NULL)
4.returnNULL;
5.if(strDest==strSrc)
6.returnstrDest;
7.char*tempptr=strDest;
8.while((*strDest++=*strSrc++)!
=‘\0’);
9.returntempptr;
10.}
复制代码
12.已知String类定义如下:
1.classString
2.{
3.public:
4.String(constchar*str=NULL);//通用构造函数
5.String(constString&another);//拷贝构造函数
6.~String();//析构函数
7.String&operater=(constString&rhs);//赋值函数
8.private:
9.char*m_data;//用于保存字符串
10.};
复制代码
尝试写出类的成员函数实现。
答案:
1.String:
:
String(constchar*str)
2.{
3. if(str==NULL)//strlen在参数为NULL时会抛异常才会有这步判断
4. {
5. m_data=newchar[1];
6. m_data[0]='\0';
7. }
8. else
9. {
10. m_data=newchar[strlen(str)+1];
11. strcpy(m_data,str);
12. }
13.}
14.String:
:
String(constString&another)
15.{
16. m_data=newchar[strlen(another.m_data)+1];
17. strcpy(m_data,other.m_data);
18.}
19.String&String:
:
operator=(constString&rhs)
20.{
21. if(this==&rhs)
22. return*this;
23. delete[]m_data;//删除原来的数据,新开一块内存
24. m_data=newchar[strlen(rhs.m_data)+1];
25. strcpy(m_data,rhs.m_data);
26. return*this;
27.}
28.String:
:
~String()
29.{
30. delete[]m_data;
31.}
复制代码
13..h头文件中的ifndef/define/endif的作用?
答:
防止该头文件被重复引用。
14.#include与#include"file.h"的区别?
答:
前者是从StandardLibrary的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h.15.在C++程序中调用被C编译器编译后的函数,为什么要加extern“C”?
首先,作为extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。
通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。
例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。
这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数extern"C"是连接申明(linkagedeclaration),被extern"C"修饰的变量和函数是按照C语言方式编译和连接的,来看看C++中对类似C的函数是怎样编译的:
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。
函数被C++编译后在符号库中的名字与C语言的不同。
例如,假设某个函数的原型为:
voidfoo(intx,inty);
该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangledname”)。
_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。
例如,在C++中,函数voidfoo(intx,inty)与voidfoo(intx,floaty)编译生成的符号是不相同的,后者为_foo_int_float.同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。
用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。
而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。
未加extern"C"声明时的连接方式假设在C++中,模块A的头文件如下
1.//模块A头文件 moduleA.h
2.#ifndefMODULE_A_H
3.#defineMODULE_A_H
4.intfoo(intx,inty);
5.#endif
复制代码
在模块B中引用该函数:
1.//模块B实现文件 moduleB.cpp
2.#include"moduleA.h"
3.foo(2,3);
复制代码
实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!
加extern"C"声明后的编译和连接方式
加extern"C"声明后,模块A的头文件变为:
1.//模块A头文件 moduleA.h
2.#ifndefMODULE_A_H
3.#defineMODULE_A_H
4.extern"C"intfoo(intx,inty);
5.#endif
复制代码
在模块B的实现文件中仍然调用foo(2,3),其结果是:
(1)模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;
(2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo.
如果在模块A中函数声明了foo为extern"C"类型,而模块B中包含的是externintfoo(intx,inty),则模块B找不到模块A中的函数;反之亦然。
所以,可以用一句话概括extern“C”这个声明的真实目的(任何语言中的任何语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。
我们在思考问题时,不能只停留在这个语言是怎么做的,还要问一问它为什么要这么做,动机是什么,这样我们可以更深入地理解许多问题):
实现C++与C及其它语言的混合编程。
明白了C++中extern"C"的设立动机,我们下面来具体分析extern"C"通常的使用技巧:
extern"C"的惯用法
(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:
1.extern"C"
2.{
3.#include"cExample.h"
4.}
复制代码
而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern"C"声明,在。
c文件中包含了extern"C"时会出现编译语法错误。
C++引用C函数例子工程中包含的三个文件的源代码如下:
1./*c语言头文件:
cExample.h*/
2.#ifndefC_EXAMPLE_H
3.#defineC_EXAMPLE_H
4.externintadd(intx,inty);
5.#endif
6./*c语言实现文件:
cExample.c*/
7.#include"cExample.h"
8.intadd(intx,inty)
9.{
10.returnx+y;
11.}
12.//c++实现文件,调用add:
cppFile.cpp
13.extern"C"
14.{
15.#include"cExample.h"
16.}
17.intmain(intargc,char*argv[])
18.{
19.add(2,3);
20.return0;
21.}
复制代码
如果C++调用一个C语言编写的。
DLL时,当包括。
DLL的头文件或声明接口函数时,应加extern"C"{ }.
(2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern"C",但是在C语言中不能直接引用声明了extern"C"的该头文件,应该仅将C文件中将C++中定义的extern"C"函数声明为extern类型。
C引用C++函数例子工程中包含的三个文件的源代码如下:
1.//C++头文件cppExample.h
2.#ifndefCPP_EXAMPLE_H
3.#defineCPP_EXAMPLE_H
4.extern"C"intadd(intx,inty);
5.#endif
6.//C++实现文件cppExample.cpp
7.#include"cppExample.h"
8.intadd(intx,inty)
9.{
10.returnx+y;
11.}
12./*C实现文件cFile.c
13./*这样会编译出错:
#include"cExample.h"*/
14.externintadd(intx,inty);
15.intmain(intargc,char*argv[])
16.{
17.add(2,3);
18.return0;
19.}
复制代码
15题目的解答请参考《C++中extern“C”含义深层探索》注解:
16.关联、聚合(Aggregation)以及组合(Composition)的区别?
涉及到UML中的一些概念:
关联是表示两个类的一般性联系,比如“学生”和“老师”就是一种关联关系;聚合表示has-a的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责,如下图所示,用空的菱形表示聚合关系:
从实现的角度讲,聚合可以表示为:
classA{...} classB{A*a;.....}
而组合表示c