HMODULE 模块的句柄(一般是GetModuleHandle()的返回
HINSTANCE 实例的句柄(应用程序实例
定义是这样的:typedef HINSTANCE HMODULE;
再看看HINSTANCE
typedef HANDLE HINSTANCE;
那HANDLE又是什么呢?
typedef PVOID HANDLE;
PVOID又是什么呢?
typedef void *PVOID;
勥的兲蛋
炛耄
http://wenku.baidu.com/view/b4c05942336c1eb91a375db1.html
http://blog.youkuaiyun.com/simeone18/article/details/7219509
http://blog.youkuaiyun.com/simeone18/article/details/7973100
http://www.cnblogs.com/fangyukuan/archive/2010/08/29/1811901.html
http://technet.microsoft.com/zh-cn/magazine/k3k086s0(VS.100).aspx
http://www.cnblogs.com/shipfi/archive/2007/02/15/651294.html
http://blog.youkuaiyun.com/simeone18/article/details/7224773
http://blog.youkuaiyun.com/shb8845369/article/details/23184869
http://blog.youkuaiyun.com/zhubc/article/details/4506708
http://www.cnblogs.com/mumuliang/archive/2010/07/27/1873484.html
http://hi.baidu.com/wayright/item/6094955aaa6b90cfd3e10ccc
http://blog.youkuaiyun.com/chshplp2008/article/details/4766099
http://bbs.youkuaiyun.com/topics/240017538
argument实参
int* x与int *x是一样的,但推荐后面这种写法!
比如 int* x,y;这样写的话,别人会误解为y也是指针了
而int *x,y;这里一看就知道x是指针,y不是。
如果a的定义为: char *a;
*a = 0;把a地址开始的一个字节清0;
*(int *) a = 0;把a地址开始的四个字节清0;
可以(int*)强调的强转成是四个字节的指针
所以有些特殊符号做为编译器的保留字符用来区分函数名
派生继承之后肯定有父类和自己的虚函数啦
父类的指针范围不够 子类的大!!不能调用子类没覆盖的函数.
派生类为什么有虚表,很简单,因为是继承而来的虚表.*__vptr
父类的指针只能指向子类 真实存在 的对象,自己基类基本没对象可指.但也可以调用 只是虚表没指向A::f() 即A的函数. b.A::f() 但 这样的话,b对象依然可以找到b对象内存里的A::f()函数.可能是子类虚表地址再加上适当的指针地址就得到父类的函数地址就可以引用了.
别被虚表搞糊涂了,父的函数还是会继承在内存中的,只是虚表放在子类对象的头位置而已.
重点应该是 访问派生类中同名覆盖成员函数
虚函数必须是基类的非静态成员函数,其访问权限可以是private或protected或public
隐藏与覆盖:
隐藏就是还可以通过手段把函数重现,A::f()
虚表的那些就叫覆盖
**哪个类型的对象就调用哪个类型内存中的各种函数.但是因为有虚表会被覆盖函数,所以才有覆盖的时候是调用子类的函数(多态).
外部CA::F(){}实现虚函数的时候不加virtual. 声明的时候不加virtual的时候是你自己的问题,可加可以不加,但外部实现时不加不会错,加了就反而错.
IUnknown *pUnknown 最多是这样了,因为抽象类不能实例化对象,但可以声明抽象类的指针指向派生类。
reinterpret_cast 任意32bit类型转换成指针
static_cast 只能指针转换成指针 或者是 实例本身转换成实例本身
p->B::fun()
类型变量名字 &变量名字就是地址,可以用指针指向了
好比:int a; int *p=&a; a也是一个对象的地址,但不是地址类型应该是符号
变量名字一般趋向于值(返回值)多而不是地址.很好解释了函数指针void (*fun)(void)中的fun单用的时候表示 执行函数本身到返回值结束.
空类型的指针(void*)只代表不能偏移,不知道偏移量
int i;i是代表一个空间;&i表示i所代表的空间地址;
a是数组头部第一元素地址,&a 是整个数组地址(虽然值相同,意义不同),
a是个int*类型的数据, int*类型即指针类型是没初始化都能输出的..int *p;而&p才是int*类型.已证明,所以a最少是已经是&(a)(但正确是&(*a),至少说明带是带&的)
***分辨是不是指针类型就看sizeof(p)大小,如char i; i就肯定不是4,非指针类型.
struct结构体类型的变量名并不能直接当作地址使用,这一点和基本数据类型相同。
int *p是在栈区分配了4字节一个指向未知的空间
函数本身也像基本数据类型: int f(int);int(*fp)(int)=&f; 单用函数符号名趋向于返回值
p="string";字串是常量(在静态区),但p=1;(错误,非常量) . int i=1;int *p=(int*)i;cout<<p<<endl; 会显示00000001
但 p=NULL(即0,正确); 一个表示0值的整数常量,叫做空指针常量.int *pi = NULL;中的NULL,是会被自动转换为(void *)0的
void *不能进行自增自减
void *p; int *pi; float *pf; p = pf; // pf = p;就是非法的,不能将 "void *" 类型的值分配到 "float *" 类型的实体;(即float*不能指向void*)
指针指向一个地址是为了得到该地方值(即解引用,NULL位置不能解引用)
#define NULL ((void *)0)(但不是void*不能赋值给其它类型*吗,0是常量吧转换成指针类型?) 两个定义中这个比较直观和重要,体验出为什么初始化NULL的时候内容为00000000
fun只不过也是函数指针类型的变量名而已
由函数名指向这个入口地址,函数名相当于一个指向其函数入口的指针常量(指针是常量. 不可变 可变)
前面是形容词,后面的被修饰的名词才是重点
指针函数(返回指针的函数,函数指针(指向函数的指针
指针常量(指针是常量, 常量指针(指向常量的指针 . 这就好理解了
http://wenku.baidu.com/view/94ec3176a417866fb84a8ec1.html
比较重要是看返回类型, int* 还是double,很大程度看出后面放的是什么
2txt
这里差不 多是前后数反转放, 4个字节4次交换
void swap(void *pvData1, void *pvData2, int iDataSize)
{
unsigned char *pcData1 = NULL;
unsigned char *pcData2 = NULL;
unsigned char ucTmp1;
pcData1 = (unsigned char *)pvData1;
pcData2 = (unsigned char *)pvData2;
do{
ucTmp1= *pcData1;
*pcData1 = *pcData2;
*pcData2 = ucTmp1;
pcData1++; pcData2++;
}
while (--iDataSize > 0);
}
int main()
{
float fa = 1.23, fb = 2.32;
float *f1=&fa, *f2=&fb;
int iDataSize = sizeof(float)/sizeof(char);
swap(f1, f2, iDataSize);
return 0;
}
你没明白我的意思
int a,a是变量名,加上typedef,a是int的别名;
void (*funcptr)(), funcptr同样只是一个变量名而已,只不过他的类型是void(*)(), 加上typedef之后,funcptr变成了这个类型的别名。
有些地方是必须要用typedef的,不用他完成不了某些任务。
声明函数指针就是很怪的。
如果仿照变量类型声明,声明函数指针似乎应该这样:
typedef void(*)() variable;
但是c标准的创建者没有这样做,估计怕引起歧义(没查证),所以选择:
typedef void(*variable)();
c编译器非常清楚,这就是在声明一个void(*)() 类型的函数指针variable。
还有数组指针 指针数组
+指向
+是
指向 常量/函数 的指针
指针 是 常量/函数
除了中继帧路由外..还想到一个方法就是用手机做一个热点..呵呵
void hello(void){printf("hello!");}
void bye(void){printf("bye!");}
void ok(void){printf("ok!");}
typdef void (*funcptr)(void);
void speak(int id)
{
funcptr words[3]={&hello,&bye,&ok};
funcptr fun=word[id];
(*fun)();
}
3txt
pointer to array
int i=int(*pa)[5];//注意返回值是相同的
int*(*a[5])(int,char*) 返回值 是指针类型, 声明是指针元素的数组,相对应,也可以返回的是非指针类型...每一个数组里的元素这里是指向函数的指针
void(*b[5])(int,char*); 跟上面差不多,返回值是void , 每一个元素都是void*
void(*b[5])(void(*)()); 参数是函数指针而已,每一个元素都是void *
数组一般想用typdef之前忽略不看就可以代入变量名了
double(*)()(*pa)[9] 这个就最屌了,返回类型是double(*)(),有9个元素,第一个元素都是double(*)()
对于这种理解很重要: typedef (int *) pINT;
#3. double(*)()(*pa)[9];
typedef double(*PF)();
typedef PF (*PA)[9];
PA pa; //跟doube(*)()(*pa)[9];的效果一样! 3.const和volatile在类型声明中的位置。
this是关键字,属于实体(entity),this即当前对象的指针. 多态例子的时候用base* 基类的指针调用函数,而这里用this对象的指针调用函数,不外乎调用函数.
vfptr指针(void** vfptr),这个指针用来指向一个vtable表(一个函数指针数组)(一个类只有一个该表)
看下static_cast<IX*>(this)前后.this的变化 ,是整个虚表位置变了(从CA对象this去到IX的虚表),所以有Fx();其实是没变化的.已证明调试过.
是因为派生类继承所有基类__vtptr虚表 ,所以是
reinterpret_cast<IUnknown *>(*ppi)->AddRef(); 这里用static_cast<>也没分别,因为是转换成父类的指针,确保转换的是同类型指针不会有问题,只是重新解释会带无效值,而static_cast不会把无效变0.
1.因为多太态性,组件(CA)可以不停换接口.
2.接口就是一个类(抽象类),struct=class ,interface
原式: void(*b[10])(void(*)())
typedef void (*pf_taking_pfv)(pfv); // 带参数(参数是函数指针)函数指针
pf_taking_pfv[10]; 函数指针数组
3,用基类指针引用一个派生类对象,由于派生类对象也是基类的对象,所以这种引用是安全的,
但是只能引用基类成员。若试图通过基类指针引用那些只在派生类中才有的成员,编译器会报告语法错误。(解决该问题的答案是虚函数和多态性)
1、如果你以一个"基类之指针"指向一个"派生类之对象",那么经由该指针你只能调用该基类所定义的函数
3、如果基类和派生类都定义了“相同名称之函数”,那么通过对象指针调用成员函数时,到底调用了那个函数,必须视该指针的原始类型而定,而不是视指针实际所指的对象的类型而定,这与第1点其实意义相通。
例子:
假设有个 Person 类,是基类,包括了人的一些基本属性和方法。。
再假设有个 Student 类,派生于 Person 类,新增了一个 “学号” 成员。。
Person *p = new Student(),指向基类的指针(唉,这里写基类类型不行)去指向派生类,这时可以,因为 p 是 Person 的指针,所以 p 里面是没有 “学号” 这个成员的,也就无法访问 Student 新增的 “学号” 成员,如果试图访问编译的时候就报错了。。
如果反过来,Student *s = new Person(),s 有 “学号” 成员,编译器认为访问 “学号” 这个成员是没有问题的,但是实际对象是 Person,是没有这个成员的,这样的话在运行期间实际访问到哪部分的内存也就不得而知了。。这样是很危险的。。
一句话:非虚函数(不带虚表)根据指针类型调用成员函数.
虚的话指针上下移动选虚表
左值类型决定函数.因为 A.print()同理于pA->print()
估计编译器是通过名字找函数.所以会用到(左值类型指针=&对象地址 即*左值指针值)->__vtptf->函数.
而 非虚函数直接调用 处理是 print_左传类型名如:A_XXXX()
新
虚拟继承
这是因为编译器在进行编译的时候,需要确定子类的函数定义,如CA::f是确定的,那么在编译CB、CC时还需要在编译器的语法树中生成CB::f,CC::f等标识,那么,在编译CD的时候,由于CB、CC都有一个函数f,此时,编译器将试图生成这两个CD::f标识,显然这时就要报错了。(当我们不使用CD::f的时候,以上标识都不会生成,所以,如果去掉d.f()一句,程序将顺利通过编译)
编译错误如下:所以跟文章中说法一致
error C2385: 对“f”的访问不明确
1> 可能是“f”(位于基“CA”中)
1> 也可能是“f”(位于基“CA”中)
1>g:\c primer\atl\inside com\inside com src\code\chap08\aggrgate\test_vs2008_version\shan1\shan1.cpp(559) : error C3861: “f”: 找不到标识符
| 虚基类 | | 虚拟函数 |
| 多重继承 | VS | 多态 |
虚基类:
当2个或者2个以上的父亲类继承该祖父类,然后孙子类 为了多个祖父,
所以可以在父亲类继承祖父类的时候,加上关键字virtual修饰。 就可以避免了
多个祖父。
为什么不能用虚基类的问题:
从COM对象的结构上说,前4个字节是第一个接口的指针虚表,接下去是第二个(没有数据成员),第三个接口的指针。
如果你使用了虚继承,前四个字节就不是第一个接口的虚表指针了,而是虚基类表了。。。
具体看C++对象模型嘛。。。
--------------------------------
另外,COM的组件的多继承,也不需要虚继承,首先还得看虚继承是为了什么而被创造出来的。
你继承的都是只有函数,没有成员的基类,虚继承也没有意义啊。
VS2008命令提示(必须,可能加了一些环境参数):
当前文件夹下:cl /d1 reportSingleClassLayout[ClassName] [source_name].cpp
其实就是使用命令行的不公开的编译选项, 在Visual Studio的IDE里面,可以直接
右键 cpp源码文件,然后选“property” ->"c/ c++ " -> "Command line"
里面手工添加 /d1 reportAllClassLayout 这个,然后点compile就可以了。
直接用命令行cl.exe的话,有可能头文件包含目录没设置跟麻烦。
在VS的命令行里用cl /d1 reportSingleClassLayout类名 文件名 编译就好了,比如test.cpp下的class A:
cl /d1 reportSingleClassLayoutA test.cpp
新
总结就是:非虚基类的虚函数表跟第一个继承的父类合并. 虚基类的虚函数表顶部是自身,分表
如果去掉虚继承,结果将和GCC结果一样,A,B,C都是8,D为16,原因就是VC的编译器对于非虚继承,父类和子类是共享虚函数表指针的。
虚基类继承:不共享虚函数表了 +4以上字节 正常非虚基类继承:共享虚函数表
没继承关系肯定虚函数都分表...
光猫就是解码器,有自己的规格,不全通用. 路由只是选择哪条路走.
MyClass*pc=new MyClassA;
pc->fun();
编译器估计是判断到fun是virtual 虚函数的时候就会在虚函数表中去找这个函数,所以就有多态性.
如果使用MyClassA对象直接访问fun,则不会出发多态机制,因为这个函数调用在编译时期是可以确定的,编译器只需要直接调用MyClassA::fun即可。
**会不会是判断 等号=左右对象的类型是否相同 ,如果相同就不触发多态机制,用静态编译.??
多重继承下,子类不再具有自身的虚函数表,它的虚函数表与第一个父类的虚函数表合并了。
interface IX : IUnknown
{
virtual void __stdcall Fx() = 0;
}; //这里Fx()要虚函数,放到虚函数表中寻址.这里不virtual IUnknown的基类指针调用不了.
class CA : public IX, public IY
{
//IUnknown implementation
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
......
这里为什么叫IUnknown implementation 因为IUnknown纯函数的功能是在这实现的