C/C++

问:

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个字节,内存图如下:




posted @ 2014-05-30 21:41  水煮海鲜  阅读(130)  评论(0编辑  收藏  举报