1.   分析下列程序:

#include<iostream>
using namespace std;
class Myclass {
       public:
              Myclass(inti=0) {
                     cout<<1;
              }
              Myclass(constMyclass &x) {
                     cout<<2;
              }
              Myclass& operator=(const Myclass &x) {
                     cout<<3;
                     return*this;
              }
              ~Myclass(){
                     cout<<4;
              }
};
 
int main() {
       Myclassobj1(1),obj2(2),obj3(obj1);
       return0;
}

分析:MyClass obj1(1),obj2(2),obj3(obj1);obj1(1),obj2(2)会调用构造函数MyClass(int i=0){cout<<1;}输出11;

obj3(obj1),这是一个拷贝构造,会调用拷贝构造函数MyClass(constMyClass&x){cout<<2;}输出2;

程序返回会调用析构函数~MyClass(){cout<<4;},析构的顺序与构造的顺序相反,都输出4;

所以答案是112444。

 

如果把obj3(obj1)改成obj3,那么输出结果就是111444;

如果把obj3(obj1)改成obj3,obj3=obj1;输出结果1113444;

 

2. 函数模板的声明

 

模板函数格式是先声明模板类型,然后才能使用。 函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计。它的最大特点是把函数使用的数据类型作为参数。

 

函数模板的声明形式为:

template<typename数据类型参数标识符>

<返回类型><函数名>(参数表)

{

   函数体

}

例如:

 格式是 template<class T1, class T2, ...> 返回值 函数名(参数列表){//函数体}

 

3. x=5,y=8时,C++语言表达式5-2>=x-1<=y-2的值是:1

分析:操作符优先级,其中-优先级大于>=、<=,所以结果为1.

 

4.分析下列程序:

#include<iostream>
using namespacestd;
void func(int*a,int b[]) {
       b[0]=*a+6;
}
int main() {
       int a,b[5];
       a=0;
       b[0]=3;
       func(&a,b);
       printf("%d\n",b[0]);
       return 0;
}

分析:对于一维数组来说,数组作为函数参数传递,实际上传递了一个指向数组的指针,在c编译器中,当数组名作为函数参数时,在函数体内数组名自动退化为指针。此时调用函数时,相当于传址,而不是传值,会改变数组元素的值。

 

5.分析下述程序

#include<iostream>
using namespacestd;
int main() {
       char c[5]={'a','b','\0','c','\0'};
       printf("%s",c);
       return 0;
}

分析:字符数组以\0结尾,所以扫描到'\0'字符时就返回结果。

引申一下, char c[5] = { 'a', 'b', 's', 'c','d' };printf("%s", c);//这样会数组溢出(over flow),因为C风格的字符串会默认加一个'\0'

 

6.分析下列程序

#include<iostream>
using namespacestd;
int main() {
       char str[]="glad to testsomething";
       char *p=str;
       p++;
       int *p1=reinterpret_cast<int *>(p);
       p1++;
       p=reinterpret_cast<char *>(p1);
       printf("result is %s\n",p);
       return 0;
}



分析:该题的关键是要认清楚强制类型转换后指针的类型。 

p的类型为char *,p++后p指向str数组的第2个元素即字母“l”的位置。 
p1的类型为int *,p1++后p1指向的位置增加4个字节,指向str数组中的第6个元素即字母“t”的位置。 
因此最后p的内容为“to test something”。 

reinterpret_cast是C++里的强制类型转换符。注意强制类型转换。

不同类型的指针的作用是想着指向一个数据,如果++,指向下一个数据,那么此时地址的++肯定就是加相应类型的长度,所以这里有char的加的是一个字节,int的加的是4个字节。

 

7.分析下述程序:

#include<iostream>
using namespace std;
void f(int *p,int *q) {
       p++;
       *q=*q+1;
}
int main() {
       intm=1,n=2,*r=&m;
       f(r,&n);
       cout<<m<<""<<n;
       return0;
}



分析:f(r,&n); 指针r指向m的地址。 调用该函数时,将m和n的地址复制一份赋值给指针p和q; 在函数体中,将指针p加1,即p指向m的下一个地址,函数调用结束后该指针即被释放,m的地址不变; 在函数体中,将指针q所指内容加1,即n+1,这样就间接通过q改变了n的值。

 

8. 以关键字virtual的成员函数称为虚函数,主要是用于运行时多态,也就是动态绑定虚函数必须是类的成员函数,不能是友元函数、也不能是构造函数【原因:因为建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数,直到自己的构造函数,不能选择性的调用构造函数】

不能将虚函数说明为全局函数,也不能说明为static静态成员函数。因为虚函数的动态绑定必须在类的层次依靠this指针实现。

 

再添加一点:

虚函数的重载特性:一个派生类中定义基类的虚函数是函数重载的一种特殊形式。重载虚函数:要求函数名、返回类型、参数个数、参数类型和顺序都完全相同。

重载一般的函数:函数的返回类型和参数的个数、类型可以不同,仅要求函数名相同;

 

纯虚函数:是在基类中说明的虚函数,它在基类中没有定义,要求所有派生类都必须定义自己的版本。纯虚函数的定义形式:

virtual 类型 函数名(参数表)=0,该函数赋值为0,表示没有实现定义。在基类中定义为0,在派生类中实现各自的版本。

 

纯虚函数与抽象类的关系: 抽象类中至少有一个纯虚函数。

如果抽象类中的派生类没有为基类的纯虚函数定义实现版本,那么它仍然是抽象类,相反,定义了纯虚函数的实现版本的派生类称为具体类。

抽象类在C++中有以下特点

抽象类只能作为其他类的基类; 抽象类不能建立对象;抽象类不能用作参数类型、参数返回类型或显示类型转换。