2012年4月27日

COM接口指针很危险,因为使用过程中需要每一个使用者都要严格并且正确的AddRef和Release,一旦出现问题,就会造成对象

不能被正常释放,或者对象被重复删除,造成程序崩溃。所以使用COM接口,必须小心翼翼才行。
但是,即使所有的代码中,都正确的AddRef和Release,也不一定能保证万无一失,例如:
void SomeApp(IHello * pHello)
{
IHello * pCopy = pHello;
pCopy->AddRef();
OtherApp();
pCopy->Hello();
pCopy->Release();
}
看起来无懈可击,但是假设OtherApp中抛出了异常,那么pCopy->Release不就被跳过去了吗?
幸好所有的问题都从简单到复杂,再从复杂到简单的,因为我们有CComPtr。

CComPtr被称为智能指针,是ATL提供的一个模板类,能够从语法上自动完成AddRef和Release。
CComPtr的用法简单,以IHello*为例,将程序中所有接口指针类型(除了参数),都是用CComPtr<IHello>代替即可。即程序

中除了参数之外,再也不要用IHello*,全部以CComPtr<IHello>代替。

CComPtr的用法和普通COM指针几乎一样,另外是用中有以下几点需要注意。
1、CComPtr已经保证了AddRef和Release的正确调用,所有不需要,也不能够在调用AddRef和Release。
2、如果要释放一个智能指针,直接给它赋值NULL。
3、CComPtr本身析构的时候会释放COM指针。
4、当对CComPtr是用&运算符(取地址)的时候,要确保CComPtr为NULL。(因为通过CComPtr的地址对CComPtr赋值时,不会自

动调动AddRef,若不为NULL,则前面的指针不能释放,CComPtr会使用assert报警)。

以刚才的程序为例:
void SomeApp(IHello * pHello)
{
CComPtr<IHello> pCopy = pHello;
OtherApp();
pCopy->Hello();
}
由于pCopy是一个局部的对象,所以即使OtherApp()抛出异常,pCopy也会被析构,指针能够被释放。
如果不想再程序临近发布前,还因为COM指针的引用技术造成崩溃的话,就牢记这一点:程序中除了参数之外,不要直接使用

COM指针类型,一定要全部以CComPtr<IXXX>代替。

posted @ 2012-04-27 09:43 BreakMind 阅读(85) 评论(0) 编辑

2012年4月6日

几个月之前,我在一家小的软件公司上班。在我来到这家公司的时候,这家公司里面负责C++开发的程序员老L是一个二把刀。原来他是做C#开发的,后来不知道为什么来到这家小公司做C++开发的。我来到公司的第一天就开始了加班的生活,比较累,也很郁闷。做IT的,加班,倒也算可以接受,但是有一点,让很多人会非常的郁闷。那就老L这人为人做事,实在让人有点郁闷。他从不让人修改他写过的代码,这是让我非常郁闷的。为此我和他也吵过,但是死活不相信,坚决不让别人修改他写过的代码。

 

我不知道大家在遇到这样的情况该怎么办?也许大家有一些技术上的高招,也许和我一样没技术含量一般和他斗斗嘴,然后偷偷改几个必须修改的类。但是事情过去了两个月左右了,当我再次回忆此次事情的时候,我终于觉得还是自己技术太菜。如果要是我的技术更高一点,或许摩擦会少一点。特别是我开始阅读设计模式这些书之后,再来回顾那些曾经老人们说过的话,写代码要“高内聚,低耦合”。心里自然会明亮了很多,感叹老人们的技术的厉害。好的代码,能够减少两个程序员之间很多的矛盾。

 

言归正传。对于工厂模式,之前看了一点,囫囵吞枣。终于决定再次看一遍,虽挑灯夜战,但是也觉得很幸福。人只有在经历过磨难,才觉得普通的生活就是幸福。所以再次劝大家看书,务必要细心。浮躁的人成不了大事。

工厂模式分为普通工厂模式和抽象工厂模式。相比于普通工厂模式,抽象工厂模式能够做到更加的低耦合。下面我的例子是模仿大话设计模式里面的例子。

学雷锋做好事:

我们现在有一个雷锋类,雷锋类里面有一个成员函数做好事(函数)。

class Leifeng
{
public:
	Leifeng(){cout<<"<Leifeng>"<<endl;}
	virtual ~Leifeng(){cout<<"<~Leifeng>"<<endl;}

public:
	virtual void DoGoodThing() = 0;
};

 当然,雷锋做好事,全中国只有一个雷锋,不能全指望雷锋做好事。咱们也得动动手,学雷锋做好事。于是全国各个不同年级的学生都开始学做好事,小学生,中学生,高中生,大学生,都不甘落后学雷锋做好事。于是乎我们就需要从雷锋继承做好事的精神,动手做好事。

创建小学生学雷锋做好事的类

class CXiaoxuesheng: public Leifeng
{
public:
	CXiaoxuesheng()
	{
		cout<<"<CXiaoxuesheng>"<<endl;
	}
	~CXiaoxuesheng()
	{
		cout<<"<~CXiaoxuesheng>"<<endl;
	}
public:
	virtual void DoGoodThing()
	{
		cout<<"小学生,帮老太太扫地"<<endl;
	}
};

 

创建中学生学雷锋做好事的类

class CZhongxuesheng: public Leifeng
{
public:
	virtual void DoGoodThing()
	{
		cout<<"中学生,帮老太太擦玻璃"<<endl;
	}
};

 

创建高中生学雷锋做好事的类

class CGaozhongsheng: public Leifeng
{
public:
	virtual void DoGoodThing()
	{
		cout<<"高中生,帮老太太买米"<<endl;
	}
};

 

创建大学生学雷锋做好事的类

class CDaxuesheng: public Leifeng
{
public:
	virtual void DoGoodThing()
	{
		cout<<"大学生,给老太太看病"<<endl;
	}
};

 

这个时候,我们可以根据不同的学生类型来产生不同的学生对象。我们需要有一个工厂类。(具体为什么这样做,先看完了全文,然后自然会明白的)

创建一个工厂类

class CFactory
{
public:
	virtual ~CFactory(){}

public:
	static Leifeng *GetHaoren(char ch)//x,z,g,d分别表示小学生,中学生,高中生,大学生
	{
		switch(ch)
		{
		case 'x': return new CXiaoxuesheng();
		case 'z': return new CZhongxuesheng();
		case 'g': return new CGaozhongsheng();
		case 'd': return new CDaxuesheng();
		default: return NULL;
		}
	}
};

 在主函数里面我们现在可以完成调用工厂模式调用过程:

Leifeng *pLeifeng = CFactory::GetHaoren('x');//
pLeifeng->DoGoodThing();

 

 以上就是一个完整的工厂模式的设计。可以对照下面的图片理解一下:

工厂模式,说白了就是一个带有虚函数的类A(通常是纯需函数),再根据不同的情况,对该类进行派生,形成多个派生类A1,A2,A3,...,An。另外一个就是工厂类Factory,这个Factory有一个成员函数,专门根据不同的情况产生派生类An对象,并且将派生类对象的指针返回。我们定义一个类A的指针,把之前返回的值,赋值非类A的指针。这个时候,我们就可以调用类A的成员虚函数(这个时候,就调用的是派生类中虚函数的实现过程)。

 

抽象工厂模式:

抽象工厂模式,就是将工厂类再次进行抽象。

这样,我们的工厂类仅仅申明可以建立不同的做好事人员对象,并不做什么实际工作。

class CFactory
{
public:
	CFactory(){}
	virtual ~CFactory(){}
public:
	virtual Leifeng *GetHaoren() = 0;
};

 

这个时候,我们需要根据实际的情况来看,我们有小学生做好事,中学生做好事,高中生做好事,大学生做好事。因此我们需要对他们进行实例化。

class CXiaoxueshengFactory:public CFactory
{
public:
	virtual Leifeng *GetHaoren()
	{
		return new CXiaoxuesheng();
	}
};

class CZhongxueshengFactory: public CFactory
{
public:
	virtual Leifeng* GetHaoren()
	{
		return new CZhongxuesheng();
	}
};
class CGaozhongshengFactory:public CFactory
{
public:
	virtual Leifeng* GetHaoren()
	{
		return new CGaozhongsheng();
	}
};
class CDaxueshengFactory:public CFactory
{
public:
	virtual Leifeng * GetHaoren()
	{
		return new CDaxuesheng();
	}
};

 

 此时我们可以在调用的时候,就可以有如下操作:

int main()
{
	CFactory * pFactory = new CXiaoxueshengFactory();
	Leifeng * pLeifeng = pFactory->GetHaoren();
	pLeifeng->DoGoodThing();
	delete pLeifeng;
	return 0;
}

 

 以上就是学雷锋做好事抽象工厂模式,下面是完整的代码。

#include <iostream>
using namespace std;

class Leifeng
{
public:
	Leifeng(){cout<<"<Leifeng>"<<endl;}
	virtual ~Leifeng(){cout<<"<~Leifeng>"<<endl;}

public:
	virtual void DoGoodThing() = 0;
};

class CXiaoxuesheng: public Leifeng
{
public:
	CXiaoxuesheng()
	{
		cout<<"<CXiaoxuesheng>"<<endl;
	}
	~CXiaoxuesheng()
	{
		cout<<"<~CXiaoxuesheng>"<<endl;
	}
public:
	virtual void DoGoodThing()
	{
		cout<<"小学生,帮老太太扫地"<<endl;
	}
};

class CZhongxuesheng: public Leifeng
{
public:
	virtual void DoGoodThing()
	{
		cout<<"中学生,帮老太太擦玻璃"<<endl;
	}
};

class CGaozhongsheng: public Leifeng
{
public:
	virtual void DoGoodThing()
	{
		cout<<"高中生,帮老太太买米"<<endl;
	}
};

class CDaxuesheng: public Leifeng
{
public:
	virtual void DoGoodThing()
	{
		cout<<"大学生,给老太太看病"<<endl;
	}
};

class CFactory
{
public:
	CFactory(){}
	virtual ~CFactory(){}
public:
	virtual Leifeng *GetHaoren() = 0;
};

class CXiaoxueshengFactory:public CFactory
{
public:
	virtual Leifeng *GetHaoren()
	{
		return new CXiaoxuesheng();
	}
};

class CZhongxueshengFactory: public CFactory
{
public:
	virtual Leifeng* GetHaoren()
	{
		return new CZhongxuesheng();
	}
};
class CGaozhongshengFactory:public CFactory
{
public:
	virtual Leifeng* GetHaoren()
	{
		return new CGaozhongsheng();
	}
};
class CDaxueshengFactory:public CFactory
{
public:
	virtual Leifeng * GetHaoren()
	{
		return new CDaxuesheng();
	}
};

int main()
{
	CFactory * pFactory = new CXiaoxueshengFactory();
	Leifeng * pLeifeng = pFactory->GetHaoren();
	pLeifeng->DoGoodThing();
	delete pLeifeng;
	return 0;
}

 

最后,我们回过头来在看代码,我们在main函数中,只需要改动new后面的CXiaoxueshengFactory类类型,就可以产生不同的对象(比如中学生,大学生等),这个时候就可以让别的学生做其他的好事。如果我们在做好事的人里面,添加社会义工这一项,那么我们只需要继续从leifeng这个类中继承一下。然后再工厂类中继承一下,产生新类,用于生成义工这一个类对象。 我们完全不需要修改原来别人的代码,就能够完成添加不同的学雷锋群体,以及他们具体做什么好事。

 

再回头想想我的同事老L不让我修改他的代码。假设他当时能够写成这样的代码,我和他之间就没有任何的冲突可产生。还是自己的能力太弱了。呵呵

posted @ 2012-04-06 02:59 BreakMind 阅读(227) 评论(2) 编辑

2012年4月5日

进程中的线程共享的资源有哪些?在计算机操作系统原理里面讲到了进程中的线程共享进程的资源。但是对于进程具体共享哪些资源,书上讲得含糊不清,网上的资料也是比较笼统,没有确切的内容来说明到底哪些资源是共享的,哪些资源是独享的。

线程之间共享堆区资源:

线程之间共享栈区(堆栈)资源:

理由如下:

#include <iostream>
#include <Windows.h>
using namespace std;

char *g_pStr = NULL;
int *g_pData = 0;
DWORD WINAPI ThreadFunc1( LPVOID lpThreadParameter );
DWORD WINAPI ThreadFunc2( LPVOID lpThreadParameter );
int main()
{
	char *p = new char [1024];
	memset(p,0,1024);
	strcpy(p,"Hello123456");
	g_pStr = p;
	int n = 1999;
	g_pData = &n;
	HANDLE hd1 = CreateThread(NULL,0,ThreadFunc1,NULL,CREATE_ALWAYS,NULL);
	HANDLE hd2 = CreateThread(NULL,0,ThreadFunc2,NULL,CREATE_ALWAYS,NULL);
	WaitForSingleObject(hd1,INFINITE);
	WaitForSingleObject(hd2,INFINITE);
	Sleep(500);
	return 0;
}
DWORD WINAPI ThreadFunc1( LPVOID lpThreadParameter )
{
	char *p = new char [1024];
	memset(p,0,1024);
	strcpy(p,"Hello123456");
	g_pStr = p;
	int nData = 122222;
	g_pData = &nData;
	Sleep(10000);

	return 0;
}

DWORD WINAPI ThreadFunc2( LPVOID lpThreadParameter )
{
	char *pStr = g_pStr;
	if (g_pStr)
	{
		cout<<pStr<<endl;
	}
	int *pD = g_pData;
	cout<<*pD<<endl;

	return 0;
}

  

在我们的线程中,线程1中在栈区创建某个对象,在线程2中,通过线程1中的地址,直接来取出线程1中栈区中的某个对象,我们是能够取出来的。在线程2中,我们也可以取出线程1中的某个值。这也就表明了,进程中的线程共享进程中的栈区,也共享进程中的堆区。

posted @ 2012-04-05 15:38 BreakMind 阅读(138) 评论(0) 编辑

2012年4月3日

#include <Windows.h>
#include <iostream>
using namespace std;


class CMyLock 
{
public:
	CMyLock();
	~CMyLock();

public:
	bool Lock();
	bool UnLock();

private: 
	HANDLE hEvent;
};

CMyLock::CMyLock()
{

}

CMyLock::~CMyLock()
{
	CloseHandle(hEvent);
}

bool CMyLock::Lock()
{
	hEvent =  OpenEvent(EVENT_ALL_ACCESS,TRUE,L"GaoHanEvent");
	if (hEvent)
	{
		WaitForSingleObject(hEvent,INFINITE);
	}
	else
	{
		hEvent = CreateEvent(NULL,TRUE,TRUE,L"GaoHanEvent");
		SetEvent(hEvent);
	}
	ResetEvent(hEvent);
	return TRUE;
}

bool CMyLock::UnLock()
{
	SetEvent(hEvent);
	return TRUE;
}


CMyLock lock;
DWORD WINAPI SonThreadFun( LPVOID lpThreadParameter );

int main()
{

	HANDLE hThread = CreateThread(NULL,0,SonThreadFun,NULL,CREATE_ALWAYS,NULL);
	if (!hThread)
	{
		return 0;
	}
	Sleep(1000);
	lock.Lock();
	cout<<"The Father Play Roles"<<endl;
	lock.UnLock();

	WaitForSingleObject(hThread,INFINITE);

	return 0;
}

DWORD WINAPI SonThreadFun( LPVOID lpThreadParameter )
{
	lock.Lock();
	cout<<"The Son Play Roles"<<endl;
	Sleep(10000);
	lock.UnLock();
	return 0;
}

  说明:这是Windows下,利用Event(事件)来创建一个锁。这个锁能够防止访问的冲突。

#include <iostream>
#include <Windows.h>
using namespace std;

class CLock
{
public:
	CLock():hd(NULL){}
	~CLock(){CloseHandle(hd);
	}

public:
	bool Lock()
	{
		hd = OpenMutex(MUTEX_ALL_ACCESS,TRUE,L"GaoHanMutex");
		if (hd)
		{
			WaitForSingleObject(hd,INFINITE);
		}
		else
		{
			hd = CreateMutex(NULL,TRUE,L"GaoHanMutex");
		}

		return true;
	}
	bool unLock()
	{
		if (hd)
			ReleaseMutex(hd);
		return true;
	}
private:
	HANDLE hd;
};

CLock lock;
DWORD WINAPI ThreadFunc( LPVOID lpThreadParameter );
int main()
{
	HANDLE  hThread = CreateThread(NULL,0,ThreadFunc,NULL,CREATE_ALWAYS,NULL);
	Sleep(1000);
	lock.Lock();
	cout<<"父进程获得机会,开始把资源锁起来"<<endl;
	lock.unLock();
	WaitForSingleObject(hThread,INFINITE);
	return 0;
}

DWORD WINAPI ThreadFunc( LPVOID lpThreadParameter )
{
	lock.Lock();
	Sleep(10000);
	cout<<"睡了10秒,现在开始工作"<<endl;
	lock.unLock();
	return 0;
}

  以上是利用事件来创建一个锁程序。该锁程序可以在同一个进程内的不同线程之间可以使用,但是对于多个进程中的不同线程,并不能起作用。


posted @ 2012-04-03 19:06 BreakMind 阅读(29) 评论(0) 编辑

2012年3月11日

继承是一个重用和扩展现有类而无需修改该类的机制,从而在两者之间形成层级关系。

继承差不多是吧一个对象嵌入到另外一个对象。假设你在类 B 的定义中声明类 A 的一个对象 x 对象,结果,类 B 将能够访

问类 A 所有的公共的数据成员以及成员函数。然而,在类 B 中,你想要访问类 A 的所有的公共数据成员以及成员函数,需

要通过类 A 的对象 x。 下面的例子示例这些:

 1 #include <iostream>
2 using namespace std;
3
4 class A {
5 int data;
6 public:
7 void f(int arg) { data = arg; }
8 int g() { return data; }
9 };
10
11 class B {
12 public:
13 A x;
14 };
15
16 int main() {
17 B obj;
18 obj.x.f(20);
19 cout << obj.x.g() << endl;
20 // cout << obj.g() << endl;
21 }

在主函数中,对象 obj 利用语句 obj.x,f(20) 通过它的数据成员 B::x 访问函数 A::f()。 对象 obj 利用语句 obj.x.g()

以类似的方式访问 A::g()。 由于 g() 是类 A 的成员函数,而不是类 B 的成员函数,编译器将不允许语句 obj.g() 通过。

继承机制允许你使用相似于上面例子中的语句 obj.g() 。为了让该语句合法, g() 必须是类 B 的成员函数。

继承允许你将另外一个类成员的名称以及定义作为新类的一部分。某个类,你想将其成员包含到你的新类,这个类称为基类。

下面的例子和上面的例子一样,除了它用了继承机制,允许类 B 访问类 A 的成员。

 1 #include <iostream>
2 using namespace std;
3
4 class A {
5 int data;
6 public:
7 void f(int arg) { data = arg; }
8 int g() { return data; }
9 };
10
11 class B : public A { };
12
13 int main() {
14 B obj;
15 obj.f(20);
16 cout << obj.g() << endl;
17 }

类 A 是类 B 的基类。 类 A 成员变量的名称和定义都包含在类 B 的定义中。类 B 继承类 A 的成员。类 B 是类 A 的派生

类。 类 B 包含 A 类型的子对象。

你可以同样添加新的数据成员和成员函数到派生类。你可以通过覆盖基类成员函数或者数据在新派生的类中修改已经存在的成

员函数或者数据。

你可以从其他派生类中派生新类,从而建立另外一个层级的继承。下面的例子示例这些:

1 struct A { };
2 struct B : A { };
3 struct C : B { };

类 B 是类 A 的派生类,但是同时是类 C 的基类。 继承的层级仅仅受限于资源。(也就是说,只要内存足够,我们就可以无

限制派生)

多重继承允许你创建一个派生类,其从多个基类继承属性。因为一个派生类继承所有的基类成员,有可能导致混淆(形成冲突

)。例如,如果两个基类包含具有相同名称的变量,派生类不能隐式确定两个变量的差异。注意,当你使用多继承,访问基类

成员的名称可能会形成混淆。


直接基类:就是一个派生类在声明的时候,直接紧跟在“:”号后面的类。
间接基类:

1 class A {
2 public:
3 int x;
4 };
5 class B : public A {
6 public:
7 int y;
8 };
9 class C : public B { };

类 B 是一个类 C 的直接基类。 类 A 是类 B 的直接基类。 类A 是类 C 的间接基类。(类 C 拥有 成员变量 x 和 y)

Polymorphic functions are functions that can be applied to objects of more than one type.

多态函数指的是可以被应用多种数据类型的函数。在C++中, 多态函数有两种实现方式:

  • 重载函数——在编译期间静态绑定。
  • C++提供的虚函数。虚函数在运行期间动态绑定。

posted @ 2012-03-11 05:29 BreakMind 阅读(96) 评论(0) 编辑

2012年3月10日

摘要: 非类型模板参数格式和声明一个下面的类型是一样的。整型或枚举指向一个对象或一个函数引用到一个对象或函数指向一个成员把非类型的模板参数声明为一个数组或者函数,将分别被转换到成指针以及指向函数的指针。下面的例子演示这些:1 template<int a[4]> struct A {};2 template<int f(int)> struct B {};3 4 int i;5 int g(int) {return 0;}6 7 A<&i> x;8 B<&g> y;类型 &i 推导出来是 int *,而类型&g 推导出来是阅读全文

posted @ 2012-03-10 00:32 BreakMind 阅读(39) 评论(0) 编辑

2012年3月9日

摘要: 如果你有一个合适的名字来指定一个类型,并且这个名字依赖于模板的参数,请使用关键字 typename。在模板的声明和定义中只使用关键字typename。下面的例子说明使用关键字typename。1 template<class T> class A2 {3 T::x(y);4 typedef char C;5 A::C d;6 }表达式 T::x(y) 是含糊不清的。它可能是一个调用非本地参数值 y 的函数 x() ,也可能是一个声明类型为 T::x 的变量 y 。C++将把这句表达式理解为一个函数的调用。为了让编译器把这句表达式理解为一个类型,你应该在表达式的开头添加关...阅读全文

posted @ 2012-03-09 22:29 BreakMind 阅读(65) 评论(0) 编辑

摘要: C++中,对于自定义的类比如GCStudent,需要声明并且初始化一个对象。GCStudent stu;GCStudent stu1 = stu;对于第一个stu的声明,系统肯定会去调用系统默认的构造函数。但是对于第二个stu1,系统调用的构造函数并不是系统默认的构造函数,而是调用拷贝构造函数。但是如果我们这样声明GCStudent stu;GCStudent stu1;stu1 = stu;这个时候stu1和stu2都调用默认的构造函数,在stu1赋值的时候,调用系统的等号运算符重载。其原理,一个类的对象必须调用一次构造函数,而且一个运算符肯定不可能出现歧义,因此对于第一种情况,= 号在这里阅读全文

posted @ 2012-03-09 13:20 BreakMind 阅读(13) 评论(0) 编辑

2012年2月17日

摘要: 在Ubuntu 11.10版本中,有一个软件中心。在该软件中心安装软件,都会自动下载安装。你在安装的过程中没有选择路径这一选项。我的eclipse就是直接在软件中心中安装软件,但是后来想往里面添加插件,却找不到路径。查找一遍后总算弄清楚怎么一回事了。我的Eclipse安装后存放的位置:第一:可执行文件。系统默认安装在 /usr/bin 这个路径下。第二:配置文件。系统将eclipse的配置文件存放在 /etc/ 的路径下面。第三:其他文件以及文件夹。系统将用到的其他的资源或者插件等存放在 /usr/share/ 下面。在 /usr/share/eclipse路径下有三个文件夹,分别是dropi阅读全文

posted @ 2012-02-17 12:10 BreakMind 阅读(386) 评论(0) 编辑

摘要: 在Ubuntu下安装Eclipse的Hadoop 1.0.0插件。首先贴出一份邮件,邮件里面的内容告诉我们怎么可以得到Hadoop 1.0.0 版本的Eclipse(版本为Indigo 3.7版本)的插件。(1)在Hadoop 1.0.0 版本的安装目录下,系统并没有提供hadoop-eclipse-plugin-1.0.0.jar插件。但是在安装的目录下src/contrib/eclipse路径下相关的源码,通过编译该源码,可以得到该插件。(2)可以使用其他版本的插件,但是适用于 Hadoop 1.0.0 版本的插件最低版本0.20.205。(3)在csdn上面有hadoop-eclipse阅读全文

posted @ 2012-02-17 11:46 BreakMind 阅读(681) 评论(0) 编辑