1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std;
 
template <class T>
class Base
{
public:
	void set(int n){m_data = n;}
	int get(){return m_data;}
private:
	int static m_data;
};
template <class T>
int Base<T>::m_data = 0;
 
class Derived1: public Base<Derived1>{};
class Derived2: public Base<Derived2>{};
int 
main()
{
	Derived1 D1;
	Derived2 D2;
	D1.set(1);
	D2.set(2);
	cout<<D1.get()<<ends<<D2.get()<<endl;//~ 输出1 2
	return 0;
}

其实,这只是一个小把戏,用不同类型实例化后的类完全是两个类型了,当然会拥有不同的静态成员!

Tags: ,,.
1
2
3
4
5
6
7
8
9
10
11
int 
main()
{
	S * pS = new S;
	D1 * pD1 = pS;
	D2 * pD2 = pS;
	pS->foo(); //~ OK, 存在一条访问路径B->D1->S
	pD1->foo();
	//pD2->foo(); //~ 'B::foo' : no accessible path to private member declared in virtual base 'D2'
	return 0;
}
Tags: .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
//~ #include <string>
 
using namespace std;
 
class Base
{
public:
	virtual void foo()
	{
		cout<<"Base::foo()"<<endl;
	}
};
 
class Derived: public Base
{
private:
	void foo()
	{
		cout<<"Derived::foo()"<<endl;
	}
};
 
int 
main()
{
	Derived  * pD = new Derived;
	Base * pB = pD;
	pB->foo();				//~ OK, call Derived::foo()
	//~ pD->foo();				//~ oops, Derived::foo() is private!
	return 0;
}

可见,权限检查发生在名字查找之时,而虚函数表的访问不带有权限检查。

Tags: ,.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
//~ #include <sstream>
using namespace std;
 
int main()
{
	typedef vector<int> int_vector;
	typedef istream_iterator<int> istream_itr;
	typedef ostream_iterator<int> ostream_itr;
	typedef back_insert_iterator<vector<int> > back_ins_itr;
	//~ vector
	int_vector ivec;
	//~ 从标准输入读整数到vector容器
	copy(istream_itr(cin), istream_itr(), back_ins_itr(ivec));
	//~ 排序
	sort(ivec.begin(), ivec.end());
	//~ 输出到标准输出
	copy(ivec.begin(), ivec.end(), ostream_itr(cout, "\n"));
	return 0;
}
Tags: ,.

没有放在程序结束处,而是放在的代码段的起始位置,更甚,由于程序是从start开始执行的,似乎永远不会执行到这两行,程序也永远不会结束。但是这个程序却能正常结束,而且是由我们的这两行代码来结束执行的。
其中的猫腻就在于短转移指令jmp short s1。jmp指令是用来更改寄存器CS:IP来实现程序的转移的,汇编语言中一般是以标号来指明跳转的目的地址的。对于长转移指令,例如jmp dword ptr ds:[0],CPU确实是以目标地址的段基址和段内偏移来修改CS:IP的。但是对于短转移来说,却是利用当前IP与目标地址之间的的偏移量来确定的。上例中,执行jmp short s1时,IP指向下一条指令nop,与目标地址s1之间偏移量为10:

   CS:IP     机器码             汇编指令(编译器将标号替换了)
0B80:0018 B80000        MOV     AX,0000    ;s1
0B80:001B CD21          INT     21
0B80:001D B80000        MOV     AX,0000
0B80:0020 EBF6          JMP     0018           ; jmp short s1
0B80:0022 90              NOP
Tags: ,,.
1
2
3
4
5
//~ 判断字符c是否为空白符(空格、水平制表、垂直制表、换页、回车和换行符)
int my_isspace( int ch )
{
    return (unsigned int)(ch - 9) < 5u  ||  ch == ' ';
}
Tags: .
1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;
 
int
main()
{
	Derived * pD = new Derived;
	pD->foo();
	return 0;
}
Tags: ,.

函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。 在参数传递过程中需要解决两个问题:

  • 当参数大于一个时,参数的入栈顺序如何,即:从右向左亦或是由右向左。
  • 恢复堆栈的任务是由调用函数完成,还是被调用者负责。
Tags: ,,.

windows中的visual studio固然强大,但是对于平时测试用的小程序来说,建立一个工程毕竟是麻烦的。相对来说用g++在命令行下进行编译就方便多了(当然也可以用cl.exe),找到了这么一个工具,cygwin-b20,比较小巧实用。从这里下载
安装及使用方法:
直接使用可执行文件full.exe进行安装,安装目录建议选择默认路径。安装完成后,将路径C:\cygnus\cygwin-b20\H-i586-cygwin32\bin加入的系统环境变量PATH中,这样,你可以在任何目录下执行bin/的命令,其中包含了169个较为常用的linux命令

Tags: ,,,.
1
2
3
4
5
6
7
8
9
class quick_sort
{
public:
	typedef int T;
	explicit quick_sort(T* src, int beg, int end);
private:
	void sub_sort(int beg, int end);
	T* m_src;
};
Tags: ,.