首先请看如下代码:
#include <stdio.h>
int getLength(char a[]);
int main()
{
char a[9]="123456789";
char b[10]="123456789";
char *b="123456789";
printf("sizeof a[]:%d/n",sizeof(a)); /* return the length of array a,including '/0'*/
printf("After the function getLength() was called:%d/n",getLength(a));
printf("sizeof b:%d/n",sizeof(b)); /* return the length of pointer which type is int*/
return 0;
}
int getLength(char a[])
{
return sizeof(a);
}
这段代码将引起 编译错误,错误信息如下:
test_5.cpp(7) : error C2117: '123456789' : array bounds overflow
数组边界溢出这个不用做过多解释了吧。
我们将char a[9]="123456789";注释掉,继续编译。程序通过连编,结果如下:
sizeof a[]:10
After the function getLength() was called:4
sizeof b:4
这次我们发现同样是sizeof,通过函数传入参数和直接使用该参数,结果是不一样的。那么肯定是函数调用的过程中发生了什么。到底发生了什么呢?我也说不清楚,但是肯定有人能说清楚,=_=b
然后,我们对该程序进行调试,在我的机器上(以下测试均在我的机器上,你们可能会有不同结果),a的地址为0x12ff74,b的地址为0x00422fc,他们的内容为字符串123456789,注意:在9后面还有一个'/0'。
我们再在程序char *b="123456789";后面添加一句:char *c="123456789";然后再调试该程序,我们发现c的地址为0x00422fc,和b指向同一个地址。
我们再往语句char a[9]="123456789";的前面添加2行:int i,j;继续调试,我们发现i的地址为0x0012ff7c,j的地址为0x0012ff78。i和j均为局部变量,应该在栈上分配内存,按照顺序,先分配i的内存,然后分配j的内存,而i的地址大于j的地址,所以我们可以得出栈是从高地址到低地址的,也就是栈的底部在高地址端,栈的方向是向下的。
我们继续添加内容,在程序的开始加入:int *k;在return 0;之前加入k=new int(10);,这时我们可以通过调试发现执行k=new int(10);之前k的值是0xcccccccc,所有指针在初始化之前都是这个值(仅限这次测试的机器)。执行了k=new int(10);之后,k的值为0x00371000。由于new一般是在堆上分配内存,所以我们可以猜测堆就在0x00371000这附近,然后再继续加入语句:int *l;以及l=new int(20);调试发现l的地址为0x00371048。再继续加入语句:int *m;以及m=new(30);调试发现地址为0x00371090,这里我们能找到的规律就是越往后分配的内存其地址越大,所以它的方向是向上的,而且每次每次分配的内存块大小都是0x48字节。这样我们得出结论:动态分配内存按块分配,而且方向由低地址向高地址。
关于C++友元。我们需要记住的是友元不能是虚函数,因为友元不是类成员,而只有成员才能是虚函数。如果由于这个原因引起了设计问题,可以通过让友元函数调用虚拟成员函数来解决。
没有重新定义。假设创建了如下所示的代码:
class Dwelling
{
public:
virtual void showperks(int a) const;
};
void Dwelling::showperks(int a) const
{}
class Hovel:public Dwelling
{
public:
virtual void showperks() const;
};
void Hovel::showperks() const
{}
int main()
{
Hovel trump;
trump.showperks();
return 0;
}
但是调用trump.showperks(5);则发生错误。这说明在子类中重新定义基类函数不会生成函数的两个重载版本,而是隐藏了接受一个int参数的基类版本。简而言之,重新定义继承的方法并不是重载。
这引出了两条经验规则:第一,如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针(这种例外是新出现的),但是VC 6好像不支持。
第二,如果基类声明被重载了,则应在派生类中重新定义所有的基类版本。
单件模式(Singleton)。代码如下。
#include <iostream>
using namespace std;
class TheOnlyInstance
{
public:
static TheOnlyInstance* GetTheOnlyInstance();
void OtherMethod(){cout<<"other method called./n";}
protected:
TheOnlyInstance(){}
private:
static TheOnlyInstance *instance;
};
TheOnlyInstance * TheOnlyInstance::instance=NULL;
TheOnlyInstance* TheOnlyInstance::GetTheOnlyInstance()
{
if(instance==NULL)
{
instance=new TheOnlyInstance;
return instance;
}
else return instance;
}
int main()
{
TheOnlyInstance *pt=TheOnlyInstance::GetTheOnlyInstance();
pt->OtherMethod();
return 0;
}
赋值操作符重载。这点很容易犯错的,稍不注意,就让人觉得你没有经验。请看如下代码:
class BaseDMA
{
private:
char *label;
public:
BaseDMA(){};
~BaseDMA(){};
BaseDMA & operator=(const BaseDMA &rs);
};
BaseDMA & BaseDMA::operator =(const BaseDMA &rs)
{
if(this==&rs)
return *this;
delete [] label;
label=new char[strlen(rs.label)+1];
strcpy(label,rs.label);
return *this;
}
我们很容易忽略的一点就是a=a的情况。
请看如下代码
#include <stdio.h>
int main()
{
int i;
int a[3]={1,2,3};
int *b[3];
for(i=0;i<3;i++)
{
b[i]=&a[i];
printf("%d/n",b[i]);
}
printf("%d",b[1]-b[0]);
return 0;
}
它的输出如下所示(在我的机器上):
1245040
1245044
1245048
1
我就一不小心中了陷阱,将最后一个写成了4,晕了。
随后的东西留到以后继续慢慢想(to be continued)