MFC Collection Class是MFC提供的集合类。其中CMap中有一处有意思的实现。
Continue reading “MFC中的CMap”
c++陷阱之临时变量
先看代码:
我们开始都会认为在调用Say()之后,对象d的m_b成员变量会被修改为7但是结果却输出“1”,原因如下:
50,51行处出现了一个$T563,这其实是一个C++生成的临时对象汇编代码
37,38行如下:
37 _d$ = -8
38 $T563 = -12
( sizeof(Derived)==8, sizeof(Base)==4 )
上面
mov eax,DWORD PTR _d$[ebp]
mov DWORD PTR $T563[ebp],eax
这段代码是将对象d的内容拷贝到临时变量中,并且只拷贝Base中有的部分,这样做
就是所谓的“Slicing”。有些书中说这一步是由拷贝构造函数完成的。概念上是这样的,
但是实际上,编译器并没有生成一个真正意义上的拷贝构造函数。
这更进一步说明C++产生了一个临时对象作为强制转换的中间结果。然后以这个临时
对象代替我们的对象d,来调用函数Say()。那么结果自然是,临时变量的m_b被改变,
而我们的d.m_b没有发生变化
这种强制类型转换就是所谓的"向上转型",upcasting。 也叫Object Slicing。这种操作应该避免使用
C++、Java类方法绑定方式对比
c++,java 动态绑定和静态绑定的对比
/* a.cpp */
#include <iostream>
using namespace std;
class Base
{
public:
virtual void method1(){cout<<"Base Method1"<<endl;};
method2(){cout<<"Base Method2"<<endl;};
};
class Derived:public Base
{
public:
void method1(){cout<<"Derived Method1"<<endl;}
void method2(){cout<<"Derived Method2"<<endl;}
};
int main(int argc,char** argv)
{
Base *b = new Derived();
b->method1();
b->method2();
delete b;
return 0;
}
Output:
Derived Method1
Base Method2
/* A.java */
class A
{
public void method1(){
System.out.println("Base method1");
}
public void method2(){
System.out.println("Base method2");
}
public static void main(String[] args){
A a = new B();
a.method1();
a.method2();
}
};
/* B.java */
class B extends A
{
public void method1(){
System.out.println("Derived method1");
}
public void method2(){
System.out.println("Derived method2");
}
}
Output:
Derived method1
Derived method2
从上面的对比中可以看出
c++中要覆盖父类的方法,必须再父类的被覆盖的方法前加virtual,即将改方法声明为虚函数,这样c++编译器会产生一个虚函数表
java中如果子类中的方法名和参数列表与父类中的相同,则父类的方法自动被覆盖,不需要做而外的声明
java中默认是动态绑定或叫晚绑定,所以java中要引入 final 关键词以禁止默认的动态绑定
c++中默认是静态绑定或叫早绑定,所以c++要引入 virtual 关键词以支持动态绑定
C++中的字符串常量
int main(...)
{
printf("hello world");
printf("hello world");
}
你"猜",在内存中有几个"hello world"字符串?
C++中的逻辑运算符&&和||
a&&(b=b+2,c=c+3)
cmp DWORD PTR _a$[ebp], 0 je SHORT $L221 mov eax, DWORD PTR _b$[ebp] add eax, 2 mov DWORD PTR _b$[ebp], eax mov ecx, DWORD PTR _c$[ebp] add ecx, 3 mov DWORD PTR _c$[ebp], ecx
if(a){b=b+2;c=c+3;}
cmp DWORD PTR _a$[ebp], 0 je SHORT $L221 ;&&->je mov eax, DWORD PTR _b$[ebp] add eax, 2 mov DWORD PTR _b$[ebp], eax mov ecx, DWORD PTR _c$[ebp] add ecx, 3 mov DWORD PTR _c$[ebp], ecx
a||(b=b+2,c=c+3);
cmp DWORD PTR _a$[ebp], 0 jne SHORT $L221 ;||->jne mov eax, DWORD PTR _b$[ebp] add eax, 2 mov DWORD PTR _b$[ebp], eax mov ecx, DWORD PTR _c$[ebp] add ecx, 3 mov DWORD PTR _c$[ebp], ecx
&&,||最终会被翻译成je和jne跳转
完全等价于if语句,不过当逻辑表达式作为判断条件时
if(a&&b){}
会在后面加上test eax,eax作为jmp的依据
i++,i+=1,i=i+1 哪个效率更高?
今天看到这么一道题:
i++,i+=1,i=i+1哪个效率更高?为什么
给出的解答是:
x=x+1最低,执行过程:
1)读取右边x的地址
2)x+1
3)读取左边x的地址
4)将右值给左边的x(编译器并不认为左右x的地址相同) (编译器不会这么笨吧,两个x还能有不一样的地址??)
x+=1次之,过程:
1)读取右边x的地址
2)x+1
3)将得到的值给x(因为x的地址已经读出)
x++的效率最高,过程:
1)读取右边x的地址
2)x自增1
所以x++的效率最高….
怀疑~~
不知道上面所说的过程是从什么角度来评价效率的
按我说,效率应该从产生的指令数和指令类型综合起来说
研究下列c++代码:
int
main(int,char**)
{
int i=1;
i++;
i+=1;
i=i+1;
return 0;
}
cl.exe产生的汇编代码如下:
; Line 5
mov eax, DWORD PTR _i$[ebp]
add eax, 1
mov DWORD PTR _i$[ebp], eax
; Line 6
mov ecx, DWORD PTR _i$[ebp]
add ecx, 1
mov DWORD PTR _i$[ebp], ecx
; Line 7
mov edx, DWORD PTR _i$[ebp]
add edx, 1
mov DWORD PTR _i$[ebp], edx
g++产生的汇编代码如下:
; Line 5
leal -4(%ebp), %eax
incl (%eax)
; Line 6
leal -4(%ebp), %eax
incl (%eax)
; Line 7
leal -4(%ebp), %eax
incl (%eax)
编译器生成的汇编代码可能是经过不同成都优化的
用ollydbg查看最终exe的执行代码如下:
cl.exe产生的exe
g++产生的exe代码
可见无论cl还是g++对i++,i+=1,i=i+1的翻译都是一样的
只是cl将之翻译成3条指令,g++翻译成两条
所以说所以在上面这种情况下,i++,i+=1,i=i+1的效率是一样的!
我的小程序ZoOmeAsure
为了给ZoOmeAsure(ZA)截图还真费了一番功夫,因为它就是我最常用的截图工具。。
ZoOmeAsure=Zoom & Measure。来自Photoshop中的两个工具: 放大镜和尺子。
因为在做网页时需要精确测量尺寸,经常要做的是: 截屏->打开PS->Zoom->Measure打开Photoshop代价巨大,而我所找到的屏幕尺子大多都是模拟真实的尺子,很不方便所以就写了个小工具来完成这个工作ZA启动快速,可以整个屏幕截图,然后操作就跟PS中一样了,基本的快捷键和PS默认基本相同可以保存局部为图片,也可以直接将局部拷贝到剪贴板中目前还没有发布,正在研究滚动截屏 (我舍友说,他最需要的就是一个能滚动截屏的小截图工具)
通过汇编代码研究C++
首先树立以下几个概念:
- 编译器 compiler
- 链接器 linker
- 编译时期 compile time
- 运行时期 runtime
- 数据段 data segment/ds
- 代码段 code segment/cs
- 语法 syntax
- 语义 semantics
- 栈 stack
- 堆 heap
全局变量
int global_var = 5;
int
main(int argc,char** argv)
{
static int i=3;
i=7;
return 0;
}
PUBLIC ; global_var
_DATA SEGMENT
;静态变量和全局变量在数据段分配
DD 05H ; global_var
DD 03H
_DATA ENDS
PUBLIC _main
_TEXT SEGMENT
_main PROC NEAR
push ebp
mov ebp, esp
; 直接对数据段内存赋值
mov DWORD PTR , 7
xor eax, eax
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
类的静态成员变量
class MyClass
{
public:
int static STATIC_VAR;
};
int MyClass::STATIC_VAR = 7;
int
main(int argc,char** argv)
{
MyClass::STATIC_VAR = 6;
return 0;
}
PUBLIC ; MyClass::STATIC_VAR
_DATA SEGMENT
;类的静态成员同样在数据段分配分配内存
DD 07H ; MyClass::STATIC_VAR
_DATA ENDS
PUBLIC _main
_TEXT SEGMENT
_main PROC NEAR
push ebp
mov ebp, esp
;直接对数据段内存赋值
mov DWORD PTR , 6 ; MyClass::STATIC_VAR
xor eax, eax
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
枚举
enum COLOR{
cl0,cl1,cl2,cl3,cl4,cl5,cl6,cl7,cl8,cl9,cl10,cl11,cl12,cl13,cl14,cl15,
cl16,cl17,cl18,cl19,cl20,cl21,cl22,cl23,cl24,cl25,cl26,cl27,cl28,cl29,cl30,
cl31,cl32,cl33,cl34,cl35,cl36,cl37,cl38,cl39,cl40,cl41,cl42,cl43,cl44,cl45,
cl46,cl47,cl48,cl49,cl50,cl51,cl52,cl53,cl54,cl55,cl56,cl57,cl58,cl59,cl60,
cl61,cl62,cl63,cl64,cl65,cl66,cl67,cl68,cl69,cl70,cl71,cl72,cl73,cl74,cl75,
cl76,cl77,cl78,cl79,cl80,cl81,cl82,cl83,cl84,cl85,cl86,cl87,cl88,cl89,cl90,
cl91,cl92,cl93,cl94,cl95,cl96,cl97,cl98,cl99,cl100,cl101,cl102,cl103,cl104,
cl105,cl106,cl107,cl108,cl109,cl110,cl111,cl112,cl113,cl114,cl115,cl116,
cl117,cl118,cl119,cl120,cl121,cl122,cl123,cl124,cl125,cl126,cl127,cl128,
cl129,cl130,cl131,cl132,cl133,cl134,cl135,cl136,cl137,cl138,cl139,cl140,
cl141,cl142,cl143,cl144,cl145,cl146,cl147,cl148,cl149,cl150,cl151,cl152,
cl153,cl154,cl155,cl156,cl157,cl158,cl159,cl160,cl161,cl162,cl163,cl164,
cl165,cl166,cl167,cl168,cl169,cl170,cl171,cl172,cl173,cl174,cl175,cl176,
cl177,cl178,cl179,cl180,cl181,cl182,cl183,cl184,cl185,cl186,cl187,cl188,
cl189,cl190,cl191,cl192,cl193,cl194,cl195,cl196,cl197,cl198,cl199,cl200,
cl201,cl202,cl203,cl204,cl205,cl206,cl207,cl208,cl209,cl210,cl211,cl212,
cl213,cl214,cl215,cl216,cl217,cl218,cl219,cl220,cl221,cl222,cl223,cl224,
cl225,cl226,cl227,cl228,cl229,cl230,cl231,cl232,cl233,cl234,cl235,cl236,
cl237,cl238,cl239,cl240,cl241,cl242,cl243,cl244,cl245,cl246,cl247,cl248,
cl249,cl250,cl251,cl252,cl253,cl254,cl255,cl256,cl257,cl258};
int
main(int argc,char** argv)
{
COLOR color = cl258;
color = cl2;
return 0;
}
PUBLIC _main
_TEXT SEGMENT
_color$ = -4
_main PROC NEAR
push ebp
mov ebp, esp
push ecx
; 枚举类型最终是以无符号整数实现的
; COLOR color = cl258;
mov DWORD PTR _color$[ebp], 258
; color = cl2;
mov DWORD PTR _color$[ebp], 2
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
类的构造函数
class MyClass
{
public:
int i;
int j;
int k;
MyClass();
~MyClass();
void f(int i);
};
MyClass::MyClass(){
this->i=7;
this->j=9;
this->k=11;
}
MyClass::~MyClass(){}
void MyClass::f(int j)
{
this->j = j;
__asm{
mov this,5
}
}
int
main(int argc,char** argv)
{
MyClass *my = new MyClass();
my->f(3);
return 0;
}
;-----------------------------------------------
; MyClass::MyClass
;-----------------------------------------------
PUBLIC ; MyClass::MyClass
_TEXT SEGMENT
_this$ = -4
PROC NEAR ; MyClass::MyClass
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], 7
mov ecx, DWORD PTR _this$[ebp]
mov DWORD PTR [ecx+4], 9
mov edx, DWORD PTR _this$[ebp]
mov DWORD PTR [edx+8], 11 ; 0000000bH
mov eax, DWORD PTR _this$[ebp]
mov esp, ebp
pop ebp
ret 0
ENDP ; MyClass::MyClass
_TEXT ENDS
;-----------------------------------------------
; MyClass::~MyClass
;-----------------------------------------------
PUBLIC ; MyClass::~MyClass
_TEXT SEGMENT
_this$ = -4
PROC NEAR ; MyClass::~MyClass
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
mov esp, ebp
pop ebp
ret 0
ENDP ; MyClass::~MyClass
_TEXT ENDS
;-----------------------------------------------
; MyClass::f()
;-----------------------------------------------
PUBLIC ; MyClass::f
_TEXT SEGMENT
_j$ = 8
_this$ = -4
PROC NEAR ; MyClass::f
push ebp
mov ebp, esp
push ecx
push ebx
push esi
push edi
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR _j$[ebp]
mov DWORD PTR [eax+4], ecx
mov DWORD PTR _this$[ebp], 5
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 4
ENDP ; MyClass::f
_TEXT ENDS
;-----------------------------------------------
; 主函数
;-----------------------------------------------
PUBLIC _main
;new操作符号就是一个函数
EXTRN ; operator new
_TEXT SEGMENT
_my$ = -4
$T254 = -8
_main PROC NEAR
; Line 27
push ebp
mov ebp, esp
sub esp, 12
; MyClass *my = new MyClass();
; 调用new()在堆中分配一块内存,大小为MyClass左右成员变量大小之和,this指针在计算之内
; 12入栈,12是new的参数,由于编译时期MyClass已知,所以掉用new的时候不用给出参数,由编译器自己计算
push 12
call ; operator new
add esp, 4
; new()将在堆中分配的内存块的首地址放在EAX,然后将EAX中的内容(首地址)放入变量my中
mov DWORD PTR $T254[ebp], eax
; 检查new()是否分配失败,如果失败则跳转到$L255
cmp DWORD PTR $T254[ebp], 0
je SHORT $L255
; 如果分配成功,以new()返回的首地址为参数调用MyClass的构造函数
mov ecx, DWORD PTR $T254[ebp]
call ; MyClass::MyClass
mov DWORD PTR -12+[ebp], eax
jmp SHORT $L256
mov DWORD PTR -12+[ebp], 0
mov eax, DWORD PTR -12+[ebp]
mov DWORD PTR _my$[ebp], eax
; my->f(3);
push 3
mov ecx, DWORD PTR _my$[ebp]
call ; MyClass::f
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
指针和地址
int main(int argc, char *argv[])
{
int i=7;
char c = 'c';
int *pi=&i;
char *pc=&c;
pi = (int*)pc;
return 0;
}
PUBLIC _main
_TEXT SEGMENT
_i$ = -12
_c$ = -8
_pi$ = -4
_pc$ = -16
_main PROC NEAR
push ebp
mov ebp, esp
sub esp, 16
; int i = 7;
mov DWORD PTR _i$[ebp], 7
; char c = "c";
mov BYTE PTR _c$[ebp], 99
; int *pi = &i;
lea eax, DWORD PTR _i$[ebp]
mov DWORD PTR _pi$[ebp], eax
; char *pc = &c;
lea ecx, DWORD PTR _c$[ebp]
mov DWORD PTR _pc$[ebp], ecx
; pi = (int*)pc;
mov edx, DWORD PTR _pc$[ebp]
mov DWORD PTR _pi$[ebp], edx
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
更新:
推荐下面这本书,对理解c++如何实现各种语言特性很有帮助