C++学习(37)

来源:互联网 发布:mba课程内容 知乎 编辑:程序博客网 时间:2024/05/21 05:44

1.  C++对空类或者空结构体,对其sizeof操作时候,默认都是1个字节。

 

2.  下列程序输出的结果:12

#include<iostream>using namespacestd;class A{  public:   int _a;   A(){     _a=1;   }   void print() {     cout<<_a;   }};class B:public A {  public:   int _a;   B() {     _a=2;   }};int main() {  B b;  b.print();  cout<<b._a;  return 0;}
分析:因为在继承的时候,子类存在与父类同名的成员变量,则子类会屏蔽父类的成员变量。因为给孩子类中没有定义print函数,所以会按照就近原则去寻找父类中是否有print函数。恰好父类中有这个函数,于是调用父类的print函数b.print(),而这个函数会调用父类的a变量。

 

子类公有(public)继承父类,所以子类可以通过对象访问父类的公有成员函数,由于调用的是父类的公有成员函数(该函数中的this指针存放的是父类对象的地址),所以打印的是父类A的_a。

 

如果在B类增加一个void print(){cout<<_a;}函数的话,打印就是22

 

3.  委托构造是对同一类而言,参数类型不同的构造函数可以调用字节的其他构造函数。

4.  pwrite函数是系统调用。

 

5.  下列程序输出结果:ink

#include<iostream>using namespace std;int main() {  static char*s[]={"black","white","pink","violet"};  char**ptr[]={s+3,s+2,s+1,s},***p;  p=ptr;  ++p;  printf("%s",**p+1);  return 0;}
分析:首先 s是一个指针数组,存的是字符串的首地址。

char **ptr[]是一个指针数组,不过是一个二级指针数组,存的是s这个数组中,每个元素的地址。p是一个三级指针,ptr这个二级指针数组的数组名也是一个三级指针, p++只是往后跳了一个元素的位置,*p就是第二个元素的内容,也就是指向s数组中s+3这个元素的地址,**p就得到了s数组中s+3这个元素的内容,s数组中保存的是字符串的首地址,那就得到了"pink"这个元素的首地址, 在**p+1就是这个地址向后偏移一个字节,也就指到了'i'上,所以%s,**p+1还是"ink"。

 

6.首先要明白变量初始化的顺序是其声明的顺序,跟初始化列表中的顺序无关。所以变量的初始化顺序为m_nFir(i++),m_nSec(i++),m_nThd(i++),&m_nFor(m_nThd);

i初始值为1,所以经过初始化列表初始化以后m_nFir=1,m_nSec=2,m_nThd=3,m_nFor为m_nThd的一个引用。然后i的值为4,构造函数中执行语句m_nThd=i后,m_nThd=4,m_nFor是它的一个引用,自然值也为4。输出结果m_nFir+m_nSec+m_nThd+m_nFor=11。

 

6.  一个C++语言程序是由函数组成。

 

7.  派生类强制转化成基类(dynamic_cast)会做相应成员变量和函数的裁剪,仅能访问从基类继承来的部分成员;

 

派生类可以访问基类对象的protected成员;(派生类如果要访问基类protected成员只有通过派生类对象,派生类不能访问基类对象的protected成员)

 

8.  负数求补码是取反再加1,其实和取反加1是一样的结果。即“负数的补码是原数值除符号位按位取反再加一。

 

9. 实型常量又称实数或浮点数。在C语言中可以用两种形式表示一个实型常量。

小数形式:

小数形式是由数字和小数点组成的一种实数表示形式,例如0.123、.123、123.、0.0等都是合法的实型常量。

注意:小数形式表示的实型常量必须要有小数点。

 

指数形式:

这种形式类似数学中的指数形式。在数学中,一个可以用幂的形式来表示,如2.3026可以表示为0.23026×10^12.3026×10^0 23.026×10^-1等形式。在C语言中,则以“e”或“E”后跟一个整数来表示以“10”为底数的幂数。2.3026可以表示为0.23026E1、2.3026e0、23.026e-1。C语言语法规定,字母e或E之前必须要有数字,且e或E后面的指数必须为整数。如e3、5e3.6、.e、e等都是非法的指数形式。注意:在字母e或E的前后以及数字之间不得插入空格。

程序运行的过程中,其值不能被改变的量称为常量。常量有不同类型,其中12、0、-5为整型常量。'a''b'为字符常量。而4.6、-8.7则为实型常量。

一个实型常量可以赋给一个 float型、double 型或 long double 变量。根据变量的类型截取实型常量中相应的有效位数字。

 

9.  下列程序输出:Garbage value

#include<iostream>using namespace std;int main() {  int i=11;  int const*p=&i;  p++;  cout<<*p;  return 0;}
Garbage value为垃圾值,代表*p是没有值的东西。

 

10.构造函数和析构函数是两个特殊成员函数,这个函数名字和类名相同,构造函数在定义对象时候由系统自动调用,析构函数在删除对象时自动调用。

 

使用表达式“new”创建类对象时,系统自动调用类的构造函数,需要注意:

1)构造函数的名称必须与类名称相同;

2)构造函数没有返回值,使用不能有返回值;

3)构造函数不能声明为const类型;

4)任何类都最少有一个构造函数,若程序不提供,系统会提供一个默认构造函数,默认构造函数不带任何参数;(构造函数只能定义为public类型)

 

析构函数不是必须的,析构函数与类名相同,在前面加一个~符号,没有任何参数,不返回任何值。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译器自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

 

11.对于以下数据结构data的处理方式描述正确的是(C)

struct Node{

int size;

char data[0];

};

C:编译器会认为这是一个长度为0的数组,而且会支持对于数组data的越界访问;


分析:柔性数组,它只能放在结构体末尾,申明一个长度为0的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量,数组名这个符号本身代表了一个不可修改的地址常量(注意:数组名永远都不会是指针!),但对于这个数组的大小,我们可以进行动态分配。仔细理解后半部分,对于编译器而言,数组名仅仅是一个符号,它不会占用任何空间,它在结构体中,只是代表了一个偏移量,代表一个不可修改的地址常量!对于0长数组的这个特点,很容易构造出变成结构体,如缓冲区,数据包等等。

注意:构造缓冲区就是方便管理内存缓冲区,减少内存碎片化,它的作用不是标志结构体结束,而是扩展柔性数组是C99的扩展,简而言之就是一个在struct结构里的标识占位符(不占结构struct的空间)

 

12.记住:virtual函数是动态绑定,而缺省参数值却是静态绑定。意思是你可能会在“调用一个定义于派生类内的virtual函数”的同时,却使用基类为它所指定的缺省参数值。

 

结论:绝不重新定义继承而来的缺省参数值!(可参考《Effective C++》条款37)

对于本例:

B*p =newB; p->test();

p->test()执行过程理解:

(1)由于B类中没有覆盖(重写)基类中的虚函数test(),因此会调用基类A中的test();

(2)A中test()函数中继续调用虚函数fun(),因为虚函数执行动态绑定,p此时的动态类型(即目前所指对象的类型)为B*,因此此时调用虚函数fun()时,执行的是B类中的fun();所以先输出“B->”;

(3)缺省参数值是静态绑定,即此时val的值使用的是基类A中的缺省参数值,其值在编译阶段已经绑定,值为1,所以输出“1”;

最终输出“B->1”。所以大家还是记住上述结论:绝不重新定义继承而来的缺省参数值!

原创粉丝点击