这些题是经过整理的,如果有什么错误的话请大家即时反馈给我,我会尽快修改。
1. 什么是“引用”?申明和使用“引用”要注意哪些问题?
答:引用就是某个目标变量的"别名"(alias),对引用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
引发问题:
1) const 引用和变量是占同一单元吗?
答:不占同一空间。
例:
const int a = 10;
const int &ref = a;
cout << &a << endl; // 0012FF7C
cout << &ref << endl; // 0012FF74
2) const 引用可以引用什么变量?
答:既可以引用const变量,也可以引用非const变量。
const int a = 10;
const int &ref = a;
int a = 10;
const int &ref = a;
3) 是否可以定义指针的引用和引用的指针?
答:可以定义指针的引用,但不能定义引用的指针。
例:
int a = 10;
int *p = &a;
int * (&q) = p; //正确,q为对p的引用,即对指针的引用
int & (*q) = p; //错误,q为指向引用的指针
4) 对数组的引用的写法是什么?
答:
int a[5] = {0};
// 注意ref两边括号不可缺少,并且[]中值必须与所引用的数组长度相同,否则出错(见下例)
int (&ref)[5] = a;
5) 是否可以定义引用的数组?
答:不可以
int a[5] = {0};
int &ref[5] = a; //错误
2. 将“引用”作为函数参数有哪些特点?
答:(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果.但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
补充:拷贝构造函数调用的三种情况:
a) 用一个对象初始化另一个对象;
b) 函数返回一个对象;
c) 函数传递对象作为函数参数。
3. 在什么时候需要使用“常引用”?
答:如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
常引用声明方式:const 类型标识符 &引用名 = 目标变量名;
Eg:
int a ;
const int &ra = a;
ra = 1; // 错误
a = 1; // 正确
string foo( );
void bar( string & s );
那么下面的表达式将是非法的:
bar( foo( ) );
bar( "hello world" );
原因在于foo( )和 "hello world" 串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型(string& s),这是非法的。
引用型参数应该在能被定义为const的情况下,尽量定义为const 类型。
4. 将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
答:格式:类型标识符 &函数名(形参列表及类型说明){ // 函数体 }
好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生错误!)
规则:
(1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了无所指“的引用,程序会进入未知状态。 当一定要返回时,可以将局部变量声明为static。
(2)可以返回函数内部new分配的内存的引用,但是注意需要手动释放。(见下例)
(3)可以返回类成员的引用,但最好是const。
(4)流操作符重载返回值申明为“引用”的作用:流操作符 << 和 >>,这两个操作符常常希望被连续使用。
(5)在另外的一些操作符中,却千万不能返回引用:+、-、*、/ 四则运算符。它们不能返回引用,主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。
Eg1:
int& fun()
{
int * p = new int(4);
cout << p << endl; // 00382C08
cout << *p << endl; // 4
return *p;
}
int main()
{
int & ref = fun(); // *p的引用
cout << &ref << endl; // 00382C08
cout << ref << endl; // 4
//释放
int *p = &ref;
delete p; // delete &ref;
return 0;
}
5. “引用”与多态的关系?
答:引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。
Eg:
class A;
class B : class A{...};
B b;
A& ref = b;
6. “引用”与指针的区别是什么?
答:引用是某个变量的别名。把引用绑定在变量上,引用不是变量,必须给它赋值。不能为空。指针是一种特殊的变量,指针的值是某个变量的地址值,指针可以等于NULL。
一是考虑到存在不指向任何对象的可能,在这种情况下,能够设置指针为空,使用指针;如果不存在指向NULL的可能,使用引用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高 因为在使用引用之前不需要测试它的合法性。
二是需要能够在不同的时刻指向不同的对象,在这种情况下,能改变指针的指向,那么应该使用指针;如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么应该使用引用。
A* a = new A;
A* Const pa= &a; // pa指向a后,其值不能改变
7. 什么时候需要“引用”?
答:
a) 拷贝构造函数的参数:
b) 赋值操作符“=”的返回值
c) 赋值操作符“=”的参数
d) 流操作符“<<”和“>>”
8. 结构与联合有和区别?
答:
1) struct和union都是由多个不同的数据类型成员组成, 但在任何同一时刻, union中只存放了一个被选中的成员,而struct的所有成员都存在。在struct中,各成员都占有自己的内存空间,它们是同时存在的。一个struct变量的总长度等于所有成员长度之和。在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度。
2) 对于union的不同成员赋值,将会对其它成员重写,原来成员的值就不存在了,而对于struct的不同成员赋值是互不影响的。
结构体是多个数据的集合,可以保存多个数据;而联合union只保存一个数据,但是可以按照不同类型来读取。
比如你的联合里面有一个int和一个float,你第一次写进int,然后第二次写进float,那么int就不可用了(或者说读出来已经不是你存的int值了),因为这个union所在的内存区域值已经是第二次写进的float了。
Eg:
#include <iostream>
using namespace std;
union A
{
int b;
float c;
};
int main()
{
A a;
a.b = 1;
a.c = 2;
cout << a.b <<endl; // 1073741824,使用%d的方式查看浮点数2的存储格式
cout << a.c <<endl; // 2
return 0;
}
//结构和联合的占用空间计算
#include <iostream>
using namespace std;
union UnionA
{
double i;
int k[5];
char d;
};
struct StructA
{
int cat;
UnionA cow;
double dog;
} ;
struct StructB
{
int c;
char f;
double b;
};
int main()
{
cout << sizeof( UnionA ) << endl; // 24
cout << sizeof( StructA ) << endl; // 40
cout << sizeof( StructB ) << endl; // 16
return 0;
}
9. 已知String类定义如下:
class String
{
public:
String(const char *str = NULL); // 通用构造函数
String(const String &another); // 拷贝构造函数
~ String(); // 析构函数
String & operater =(const String &rhs); // 赋值函数
private:
char *m_data; // 用于保存字符串
};
实现如下:
String::String(const char *str)
{
if( !str )
{
m_data = NULL;
}
//或者 if( str == NULL )
// {
// m_data = new char[1];
// m_data[0] = ‘\0’;
// }
else
{
m_data = new char[strlen(str)+1];
strcpy( m_data, str );
}
}
String:: String(const String &another)
{
m_data = new char[strlen(another.m_data)+1];
strcpy(m_data, another.m_data);
}
String::~String()
{
if( m_data )
{
delete []m_data;
m_data = NULL;
}
}
String& String::operator = (const String &rhs)
{
//可能会出现String s(“123”); s = s;这种情况,所以需要先判断是否是本身
if( rhs == *this )
{
return *this;
}
if( m_data )
{
delete []m_data;
m_data = NULL;
}
m_data = new char[strlen(rhs.m_data)+1];
strcpy(m_data, rhs.m_data);
return *this;
}
10. .h头文件中的ifndef/define/endif 的作用?
答:ifndef/define/endif是预处理命令中的条件编译,作用是防止头文件被重复引用。
11. #include<file.h> 与 #include "file.h"的区别?
答:#include<file.h>调用标准库函数,#include”file.h”先调用用户自定义函数,如果没有找到,会调用标准库函数。
12. 面向对象的三个基本特征,并简单叙述之?
答:
1) 继承:继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并它允许程序员在保持原有类特性的基础上进行扩展,增加功能,使之更适合特殊的需要。
2) 封装:封装(Encapsulation)是面向对象程序设计最基本的特性,把数据(属性)和函数(方法)合成一个整体,对数据的访问只能通过公有的接口访问,封装的对象,这些对象通过一个受保护的接口访问其他对象。
3) 多态性:多态性(polymorphism)多态性是指允许不同类的对象对同一消息作出响应。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题,函数的重载,运算符的重载,属于多态性中的编译时的多态性,虚函数属于运行时多态。
posted on 2008-10-20 22:12
symbian-beginner 阅读(1519)
评论(0) 编辑 收藏 引用 所属分类:
C\C++