C++学习(40)

1.  分析下列程序

(1

#include<iostream>
#include<string.h>
using namespace std;
int main() {
  int*(*a)[3][4]={0};
  cout<<sizeof(*a);
}

(2

#include<iostream>
#include<string.h>
using namespace std;
int main() {
  int*a[3][4]={0};
  cout<<sizeof(a);
}

(3

#include<iostream>
#include<string.h>
using namespace std;
int main() {
  inta[3][4]={0};
  cout<<sizeof(a);
}

2. 题意为输入设定全部是大写(ASCII码A-Z为65-90,递增),所以有两种情况:

一、count[0;25]存储A-Z的个数,即count[0]存储A的个数,于是(1)++count[a[i]-'A'];(2)'A'+i,count[i];

二、count[0;25]存储Z-A的个数,即count[0]存储Z的个数,于是(1)++count['Z'-a[i]];(2)'Z'-i,count[i]。

 

2.  分析下列程序:

#include<iostream>
#include<string.h>
using namespace std;
class CParent {
  public:virtualvoid Intro() {
    cout<<"I'mParent"<<endl;
    Hobby();
  }
  virtualvoid Hobby() {
    cout<<"Ilike football!";
  }
};
class CChild:public CParent{
  public:virtualvoid Intro() {
    cout<<"I'mChild"<<endl;
    Hobby();
  }
  virtualvoid Hobby() {
    cout<<"I like basketball!";
  }
};
int main() {
  CChild*pChild=new CChild();
  CParent*pParent=(CParent *)pChild;
  pParent->Intro();
  return0;
}
一个类如果定义了虚函数,则不管是类中还是类外,对这个函数的调用都是通过对象的虚函数表,所以Intro(){hobby()};相当于vptr[0](this*){this->vptr[1](this)},虚函数表的第一二项分别是Intro()和hobby()的地址,而且子类已经把这两个都重写了,所以调用的自然都是子类的函数。

 

CParent *pParent = (CParent *)pChild;

这个是上行转换,即把子类的引用或指针转换为基类表示。这个是安全的。

所以上面的语句的作用是把pChild指针转换为基类指针并赋值给pParent,但是它指向的内容是没变的,所以最终结果是pChild指针和pParent指针都指向了child类。

而且由于基类的方法都已经声明为了虚函数,所以最后pParent->Intro();根据虚函数表就是调用了child类里面的方法。

 

每一个虚函数对象对应一个虚表指针

类和虚表对应对象和虚表指针对应;

不管怎么转换,对象没变,对象对应的虚表指针就不变

虚函数的作用是可以通过基类指针或引用 访问基类和派生类中的虚函数,从而实现动态关联。如此,基指针指向哪个类的对象就调用哪个类的函数。如果不加virtual,就变成了静态关联.

 

3.  调用Play函数需要将5隐式类型转换为Play函数中的形参b,会调用B的B(int i): data(i),打印“constructed by parameter5”。Play函数返回时需要调用B的复制构造函数给对象temp初始化。Play函数返回后需要调用b的析构函数,将Play函数的形参释放,打印“destructed”。 main函数返回后需要释放temp,打印“destructed”。

 

4.  结构体在完成定义之前是incompletetype(不完全类型),不完全类型不能定义对象,只能定义引用和指针,或者用于声明函数的形参和返回值类型

 

struct成员类型不可以是它自己。因为会递归定义。理论上这样导致结构体的大小不能被计算(无限大小)。所以不能在结构体里的成员类型是结构体本身。但是成员可以定义为该结构体的指针。就像你上面这段代码。因为指针的大小是已知的(随编译器和操作系统而定)。所以可以定义为该结构体的指针,但不是该结构体。

 

5.  分析下列程序:

#include<iostream>
#include<string.h>
using namespace std;
int main(void) {
  http://www.taobao.com
  cout<<"hello!"<<endl;
  return0;
}
分析:http在此处相当于label,意思是此处可以用go to heep;来跳转到http这个label标注的语句。

双斜杠之后的www.csdn.net被当做注释了,那么前面的http:是否合法?这就是C++中一个几乎不会被用到的语法,标签

带标签的语句是一种特殊的语句,在语句前面有一个标识符(即标签,上段代码中的http)和一个冒号。使用gotolabel就可以跳到标签处执行,比如可以在代码中写goto http,这样就会执行cout语句了。

 

case就是一种标签,case关键字和它对应的值一起,称为case标签。

类中的public、private、protect也是标签,称为成员访问标签。

 

6.  分析下列程序:没有输出结果,程序运行出错。

#include<iostream>
#include<string.h>
using namespace std;
class parent {
  public:
    virtual voidoutput();
};
void parent::output() {
  printf("parent!");
}
 
class son:public parent {
  public:
    virtual voidoutput();
};
void son::output() {
  printf("son!");
}
 
int main(void) {
  son s;
  memset(&s,0,sizeof(s));
  parent& p=s;
  p.output();
  return 0;
}

分析一:void *memset(void *s,int ch, size_t n);函数解释:将s中前n个字节(typedef unsigned int size_t)用ch替换并返回s。作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法,通常为新申请的内存做初始化工作.

 

memset将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,

本代码中, 不虚函数链表地址也清空了, 所以p.output调用失败。 output函数的地址编程0

 

分析二:按道理来说,应该输出“son!”,但是因为对引用的指针地址进行申请内存,会清空引用内所有指针函数。所以程序运行出错,没有输出结果。

 

7.类中重载运算符的一般格式是:
类名operator运算符(参数)

 

声明类的运算符成员函数时可以省略一个参数例如,一个参数时可以不用形参,参数只用一个形参,因为成员函数有this指针表示自己。但是若是类的友元函数声明运算符时,不能省略,因为这种情况下this指针并不是该类的类型。

 

8.分析下列程序

#include<iostream>
#include<string.h>
using namespace std;
class Test{
  public:
  /* 空格填写*/  int a;
  /* 空格填写*/  int b;
  public:
    Test::Test(int_a,int _b):a(_a) {
     b=_b;
    }
};
int Test::b;
int _tmain(void) {
  Testt1(0,0),t2(1,1);
  t1.b=10;
  t2.b=10;
  printf("%u %u%u %u",t1.a,t1.b,t2.a,t2.b);
  return 0;
}
A  const const/static B  -/static C const static/static D None

 

分析:BC对于成员变量a,若它为const类型,那么必须要使用Test::Test(int_a ,int _b):a(_a)这种初始化形式,若它为普通成员变量,也可以采取Test::Test(int _a,int_b):a(_a)这种形式,所以a可以为const或者普通类型,由于b没有采取Test::Test(int_a,int_b):b(_b)这种形式,所以b一定不是const类型,有main()中的t1.b和t2.b的输出都是20可以知道,b是静态变量。

 

 

补充:const static数据成员可以在类内初始化,也可以在类外,不能在构造函数中初始化,也不能在构造函数的初始化列表中初始化。

static数据成员只能在类外,即类的实现文件中初始化,也不能在构造函数中初始化,不能在构造函数的初始化列表中初始化;

 

const数据成员只能在构造函数的初始化列表中初始化

 

普通数据成员不能在类内初始化,可以在构造函数中初始化,也可以在构造函数的初始化列表中初始化

 

9.分析下列程序:

#include<iostream>
#include<string.h>
using namespace std;
int main() {
  char *sp,s[10];
  sp="Hello";
  cout<<sp;
  return 0;
}
分析:有warning,deprecated conversion from string constant to ‘char’.但是能编译运行成功。

 

字符数组的初始化以及指向字符串的指针, B选项不能直接将字符串赋值给数组名,C选项不能将两个字符数组直接互相赋值,D选项中不能这样定义 char mark[],必须指定长度或者直接初始化。

 

10.extern声明多文件共享变量的方法总结一下:

1).在一个源文件中定义,在其他需要使用的源文件中用extern声明。(仅一处定义,多处extern)

2).在一个源文件中定义,在其对应的头文件中extern声明,在其他需要使用该共享变量的源文件中包含该头文件即可。(更加标准的做法)

 

11.C++面向对象编程语言中,以下不正确的是(AD

A接口中可以用虚方法

B 一个类可以实现多个接口

C 接口不能被实例化

D接口中可以包含已经实现的方法

 

分析:这道题正确答案AD,首先所谓的接口是指只包含纯虚函数的抽象类,和普通的抽象类含不一样。所以A不对,必须是纯虚函数。然后B是正确的没有问题。然后是C,刚才说接口是特殊的抽象类,抽象类的唯一左右就是创建派生类,不能定义抽象类的对象,所以C是正确的。对于D,接口即只包含纯虚函数的抽象类,所以D是不对的。

 

12. A选项:在组合类的析构函数中并不需要显式调用其成员对象的析构函数,因为当执行组合类的析构函数时,其数据成员也会被销毁,对于类成员对象来说,成员对象销毁时,程序会自动调用其析构函数;不过对于组合类中new的指向类成员的指针,组合类需要自己去delete该指针;

 

B选项:显然是错的,在类继承层次中,基类在上,派生类在下,所以可以自动进行向上类型转换,即可以使基类指针和引用指向派生类对象,而不可以使派生类指针和引用指向基类对象;

 

C选项:对的,构造函数可以根据参数的不同实现函数重载,而因为析构函数没有参数,对于一个类来说也是唯一的,所以是不能重载的;

 

D选项:即派生类想要重写继承来的成员函数,需要用到virtual函数,来实现动态多态。

 

13. sizeof(arr)求出了为arr的长度乘以arr类型所占的字节数,这里arr是char型的,每个数据占一个字节,所sizeof(arr)得到8;

 

 

sizeof(str)这里str是字符指针,32为系统中指针占4个字节,所以为4;strlen(str)求字符串长度,这里的字符串由字符数组得到,字符数组中赋值的是字符的ASCII

其中ASCII值为0的代表字符中的‘\0’也就是字符串结束的标志,所得到的长度为5;

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值