class wnd
{
public :
void display()
{
printf("wnd/n");
}
};
class _list //:public wnd
{
public:
_list()
{
i=1;
j=3;
}
void display()
{
printf("list/n");
}
virtual fun()
{
printf("virtual _list/n");
}
void hello()
{
printf("_list say hello/n");
}
int i;
int j;
};
class _view //:public wnd
{
public:
_view()
{
i=2;
}
void display()
{
printf("view/n");
}
_list &getlist()
{
//return *((_list *)this);
return (_list &)*this;
}
virtual fun()
{
printf("virtual _view/n");
}
int i;
};
int main(int argc, char* argv[])
{
_view v;
v.getlist().display();
printf("%d/n",v.getlist().i);
v.getlist().fun();
v.getlist().hello(); // 并没构造_list,但仍可调用_list::hello
printf("%d/n",v.getlist().j);
return 0;
}
{
public:
_list()
{
i=1;
j=3;
}
void display()
{
printf("list/n");
}
virtual fun()
{
printf("virtual _list/n");
}
void hello()
{
printf("_list say hello/n");
}
int i;
int j;
};
class _view //:public wnd
{
public:
_view()
{
i=2;
}
void display()
{
printf("view/n");
}
_list &getlist()
{
//return *((_list *)this);
return (_list &)*this;
}
virtual fun()
{
printf("virtual _view/n");
}
int i;
};
int main(int argc, char* argv[])
{
_view v;
v.getlist().display();
printf("%d/n",v.getlist().i);
v.getlist().fun();
v.getlist().hello(); // 并没构造_list,但仍可调用_list::hello
printf("%d/n",v.getlist().j);
return 0;
}
结果:
list
2
virtual _view
_list say hello
1245120 这里是个随机值,并不是预想中的3
list
2
virtual _view
_list say hello
1245120 这里是个随机值,并不是预想中的3
由上可以得出
1、对"指针"或"引用"的强制转换可以是任意的,编译器不会检查
如果改成 _list l = (_list)v; 就编译不过去
2、调用某类的公共成员函数并不需要借助类的具体实实例
比如要调用_list::hello,按c++的语法,必须先构造_list实例;
但实际上hello函数是独立于_list之外的,即使没构造_list,它也存在
你可以把它当作是一个全局函数。
例如:
假设_list::hello在内存在地址是0x40100
v.getlist().hello(); 这行代码编译器直接把它编译成对call 0x40100
1、对"指针"或"引用"的强制转换可以是任意的,编译器不会检查
如果改成 _list l = (_list)v; 就编译不过去
2、调用某类的公共成员函数并不需要借助类的具体实实例
比如要调用_list::hello,按c++的语法,必须先构造_list实例;
但实际上hello函数是独立于_list之外的,即使没构造_list,它也存在
你可以把它当作是一个全局函数。
例如:
假设_list::hello在内存在地址是0x40100
v.getlist().hello(); 这行代码编译器直接把它编译成对call 0x40100
3、v.getlist() 这行代码里面有没有构造 _list 实例
没有,确实是_view实例指针,由
printf("%d/n",v.getlist().i); 显示 2
v.getlist().fun(); 显示_list say hello
可以看出,类实例里面只包含成员变量和虚函数表,这是大家都知道的
而这行printf("%d/n",v.getlist().j); 显示1245120更说明这点
v.getlist().j的访问实际已超出了_view实例的内存范围
后来为了验证函数与数据的存储写了个测试程序:
#include "stdafx.h"
没有,确实是_view实例指针,由
printf("%d/n",v.getlist().i); 显示 2
v.getlist().fun(); 显示_list say hello
可以看出,类实例里面只包含成员变量和虚函数表,这是大家都知道的
而这行printf("%d/n",v.getlist().j); 显示1245120更说明这点
v.getlist().j的访问实际已超出了_view实例的内存范围
后来为了验证函数与数据的存储写了个测试程序:
#include "stdafx.h"
class NewClass
{
public:
void hello() {printf("%d/n", a);};
protected:
private:
int a;
};
{
public:
void hello() {printf("%d/n", a);};
protected:
private:
int a;
};
int main(int argc, char* argv[])
{
NewClass *p = NULL;
p->hello();
printf("Hello World!/n");
return 0;
}
注意,这时程序是会报错的,因为数据成员a根本不存在,访问了野指针,而如果将hello()函数改为一个空函数,则一切运行正常,因为对hello函数的寻址并非通过p的偏移量去查询的(即hello函数并不处于对象的内存结构中,估计在处理调用成员函数时,编译器是将对象的this指针作为一个默认的参数传递给了函数),而对数据p->a的寻址则是通过偏移量去找的,这样一来,NewClass对象的内存结构也就清晰了
{
NewClass *p = NULL;
p->hello();
printf("Hello World!/n");
return 0;
}
注意,这时程序是会报错的,因为数据成员a根本不存在,访问了野指针,而如果将hello()函数改为一个空函数,则一切运行正常,因为对hello函数的寻址并非通过p的偏移量去查询的(即hello函数并不处于对象的内存结构中,估计在处理调用成员函数时,编译器是将对象的this指针作为一个默认的参数传递给了函数),而对数据p->a的寻址则是通过偏移量去找的,这样一来,NewClass对象的内存结构也就清晰了