问:
int a[3]; // 一维数组 char b[3][5]; // 二维数组 float c[3][5][7]; // 三维数组
比较a+1,b+1,c+1;c[2][3][4]相对数组起始地址的偏移
答:a+1代表a[1]的地址,是一级指针,要去a+1里面的内存,只需cout<<*(a+1)即可;
b+1代表b[1]的地址,是二级指针,指向从b[1][0]开始的之后的一段内存,若要取出内容,需要用2个星号,因为是二级指针,**(b+1)代表b[1][0],*(*(b+1)+1)代表b[1][1];
c+1代表c[1]的地址,是三级指针,指向从c[1][0][0]开始的地址,若要取出内容,需要3个星号,因为是三级指针,***c代表c[0][0][0]。我们用cout<<c,cout<<*c,cout<<**c,这三次的输出结果一样,但含义不同;
这里需要注意的一个问题:由于b是char型的,用cout输出跟b有关的信息会有所不同。cout<<**b:输出b[0][0]这个字节,b[0][0]里面保存的是该字节的ASCII码;cout<<*b:输出的是从b[0][0]开始的字符串,到空字符null(ASCII码为0)为止;cout<<b:输出指针b本身的值,即b[0][0]的地址;
我们测试以下语句:
cout<<sizeof(c)<<endl; cout<<sizeof(*c)<<endl; cout<<sizeof(**c)<<endl; cout<<sizeof(***c)<<endl;得出的结果为:420 140 28 4
c是三级指针,指向整个数组区域,3*5*7=105,105*4=420
*c是二级指针,指向从c[0][0][0]开始到c[0][4][6]结束的一段,5*7=35,35*4=140
**c是一级指针,指向从c[0][0][0]开始到c[0][0][6]结束的一段,7*4=28
***c是指针所指向内容float,sizeof(***c)相当于sizeof(float)
c[2][3][4]地址偏移的计算:2*5*7+3*7+4=95,95*4=380,偏移量为380个字节
问:构造函数与拷贝构造函数相关
答:拷贝构造函数与构造函数一样,若不自定义,会调用默认的函数进行操作,默认的拷贝构造函数可以完成拷贝,默认的构造函数可以完成构造。
一旦自定义了构造函数,就不能调用默认的函数了。比如自定义了带参数的构造函数,这时去使用无参数的构造函数时会报错。构造函数里无语句也可构造对象。
一旦自定义了拷贝构造函数,同样不能调用默认的拷贝构造函数了。与构造函数不同的是,拷贝构造函数里什么都不写,此时完成不了拷贝。拷贝构造函数在以下4种情况调用:1.初始化Cat cat1(cat2); 2.初始化Cat cat1 = cat2;(cat2 = cat1非初始化而是赋值时,不调用拷贝函数,而是调用默认的赋值函数,若Cat类中有new语句,则需要自定义赋值函数,同默认的拷贝函数,默认的赋值函数也是位拷贝) 3.函数参数fun(cat1); 4.函数返回return cat1;
拷贝构造函数也是构造函数,当对象初始化Cat cat1(cat2);此时不调用构造函数,而是调用拷贝构造函数完成构造和初始化的功能。
问:静态成员变量相关
答:在类内的只是静态变量的声明,静态变量的定义必须在类外,声明:static int x; 定义:int CLASSNAME::x = 1; 在类外使用静态变量时,可以用CLASSNAME::x或者对象名.x来调用,对象名1.x和对象名2.x是一样的,因为静态变量属于整个类。
问:全局变量和全局静态变量的区别
答:他们都是在全局数据区,唯一一个重要的区别就是,static全局只对当前文件有效,不支持extern被外部引用,在开发中的全局变量应声明为static,防止其他文件同名变量对此变量的使用。
问:返回引用与返回非引用相关
答:若返回局部变量的引用,将会出问题,i和j在函数结束后生存周期到,返回它们的引用得不到想要的结果。
int &Max(int i, int j) { return i>j ? i : j; }调用函数时,函数会为 输入的实参建立一个副本,该副本属于临时变量,返回时就返回这个副本,然后将该临时变量释放。
#include <iostream> using namespace std; class CText { public: ~CText() { cout<<"析构了"<<this<<endl; } }; CText& fRefer(CText &a) { return a; } CText fNoRefer(CText &a) { return a; } int main(int arge,char*argv[ ]) { CText a; cout<<"a的地址:"<<&a<<endl; cout<<endl<<"------引用类型返回值------"<<endl; a=fRefer(a); cout<<endl<<"------非引用类型的返回值------"<<endl; a=fNoRefer(a);//此处会析构一个与a地址不一样的内存,说明析构的临时变量 cout<<endl<<"------main结束------"<<endl; return 0; }
参看下面的例子:由于重载运算符=函数返回的不是引用,所以将返回值++没有意义,返回的是临时变量,待该临时变量赋值给别的变量后,这个临时变量的使命就完成了,就被释放了。
class CText { public: ~CText() { cout<<"析构了"<<this<<endl; } CText operator++() { x++; return *this; } CText operator=(CText &obj) { x = obj.x; return *this; } private: int x; }; int main(int arge,char*argv[ ]) { CText a(1),b(2); (b = a)++; return 0; }
问:对象在内存中的布局如何
答:这是一个比较大的问题,有许多种情况,这里给出一种最简单的情况
1.类的数据成员之间的间隔是类中最大数据类型的整数倍
2.数据不能跨越间隔存储
3.成员变量的保存顺序与声明顺序有关
class A { char a; public: short b; char c; };
A中short型最大,占2个字节,所以就2字节为基本单位扩展,这里A占6个字节,我们换一下顺序就可以提高利用率:
class A { char c; char a; public: short b; };这里A占4个字节
当我们考虑继承时,情况又会如何呢?
class A { char a; public: short b; char c; }; class B:public A { public: int d; };A占6个字节,B占12个字节,不要以为A中的私有变量在B中就不会出现
继承时,要按照B的要求进行对齐,B中的int占4个字节,所以要把A占的6个字节补齐为8个字节,内存图如下: