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++中有以下特点:
抽象类只能作为其他类的基类; 抽象类不能建立对象;抽象类不能用作参数类型、参数返回类型或显示类型转换。