Singleton C ++类声明 [英] Singleton C++ class declaration

查看:89
本文介绍了Singleton C ++类声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述





我一直被困在这一个星期以上,一如既往,我想这是由于我的一些误解和事实我我正在将应用程序从VS 2003升级到VS 2010.



我现在正在升级过去开发的Windows服务。在以前的版本中,在Windows 2000 Server下运行,使用Visual C ++ 7.0(VS 2003)编译,一切正常。现在我已将其移植到Visual C ++ 2010并在Windows 2008 Server下运行它。该服务有一个警告日志类,实现为Singleton模式。



log.h

Hi,

I have been stuck at this for more than a week, as always, I suppose this is due to some of my misconceptions and the fact I am upgrading an application from VS 2003 to VS 2010.

I am now upgrading a windows service developed in the past. In the previous version, running under Windows 2000 Server, compiled with Visual C++ 7.0 ( VS 2003 ) everything works fine. Now I have been porting this to Visual C++ 2010 and running it under Windows 2008 Server. The service has a warning log class that was implemented as a Singleton pattern.

log.h

class Log
{
public:
	static Log *getInstance();
        void _dummy(){};
        // lots of methods here
private:
	Log();
	static Log *log;
};





实施遵循模式要求:



log.cpp



The implementation follows the pattern requirements:

log.cpp

Log *Log::log = NULL;
Log *Log::getInstance()
{	
	if(log==NULL)
	{		
		log = new Log();
		log->AtualizarInfoRegistro();// Access windows register here
		return(log);
	}
	return(log);
}
Log::Log()
{
}





好​​的,至今。在应用程序中,通过添加log.h标头以及每当需要调用时,多次调用此静态类Log:





Ok, so far. Within the application this static class Log is called several times, by adding the log.h header and whenever it is needed to be called:

Log::getInstance()->_dummy();// called this here as an example





只要需要真正的警告级别,应用程序就会调用此类,并且必须只创建一个循环日志。如果应用程序在try catch语句中引发异常,我会调用上面的方法_dummy,但只要程序需要执行它就会崩溃系统。我已经注释掉了对这个单例类的每次调用,它确实有效。



我认为链接器无法正确获取此静态类的地址,每当我尝试使用它只是崩溃,因为它是一个无效的指针。因此,我首先尝试通过检查它是否为NULL / nullptr来测试此Log :: getInstance()方法的返回。它也不起作用。我试图引用一个指向这个对象的全局extern指针:





The application calls this class whenever a real warning level is necessary and it must create just one circular log. If the application raises an exception within a try catch statement I call the above method _dummy, but whenever the program needs to execute it crashes the system. I have already commented out every call to this singleton class and it does work.

I assumed the linker could not be getting the address properly of this static class and whenever I try to use it it simply crashes because it is an invalid pointer. Thus, I first tried to test the return of this Log::getInstance() method by checking if it is NULL/nullptr. It does not work either. I have tried to reference a global extern pointer to this object :

extern Log* ptr;

// next everytime I want to use it I call 

ptr = Log::getInstance();

// then I check if it is NULL





我已经通过记录来检查是否正在调用Log类构造函数。



这个log类由3个ISAPI DLL和服务器使用。到目前为止,错误发生在服务的执行中。



我无法使用VS Debugger来检查这一点,我实际上无法将此系统运行到我的工作站中。 />
我实际上是在尝试学习如何使用似乎在测试工作站(不是我自己的)上运行的Windbg来调试整个事情但是我正在考虑重新编写这个没有单例模式的Log类作为前景看到IIS通过ISAPI扩展访问这个类,并且这个日志类需要做的Windows注册表访问可能会做一些奇怪的工作。



编辑:



根据这里的人的建议我已经隔离了单身人士并试图在当地进行测试。



我创建了一个简单的Singleton类:







I have checked whether the Log class constructor was being called by logging it.

This log class is used by 3 ISAPI DLLs and a server. So far the error has happened within the execution of the service.

I cannot use VS Debugger to check this, I actually cannot run this system into my workstation.
I am actually trying to learn how to debug the whole thing using Windbg that seems to run on the test workstation ( not my own ) but I am thinking about re writing this Log class without the singleton pattern as the prospect of seeing this class being accessed by IIS through the ISAPI extensions and the windows registry access this log class needs to do might do things work weirdly.



Following suggestions from people here I have isolated the singleton and tried to test it locally.

I have created a simple Singleton class:


#include <string>

class LogAlerta
{
public:
	static LogAlerta *getInstance();
	void Simple(std::string&) const ;
private:
	LogAlerta();
	static LogAlerta *logAlerta;
};


#include "LogAlerta.h"
#include <exception>
#include <iostream>

using namespace std;

LogAlerta *LogAlerta::logAlerta = NULL;

LogAlerta *LogAlerta::getInstance()
{	
	if(logAlerta==NULL)
	{		
		try
		{
			logAlerta = new LogAlerta();

		}catch ( std::exception& e )
		{
			cout << "NULL Pointer" << endl;
			return NULL;
		}
		cout << "LogAlerta instanciated and returned fine!" << endl;
		return logAlerta;
	}

	cout << "LogAlerta was already initiated" << endl;
	return logAlerta ;
}

LogAlerta::LogAlerta()
{
	cout << "Constructor called" << endl;
}

void LogAlerta::Simple(std::string& _s ) const 
{
	cout << "Simple method called " << _s << endl;
}





然后,我创建了一个虚拟类,尝试使用外部指针引用来使用它:





Then, I created a dummy class to try to use it using the external pointer reference:

class Dummy1
{
public:
	Dummy1(void);
	virtual ~Dummy1(void);
	void DummySimple()const;
};

#include "Dummy1.h"
#include "LogAlerta.h"
#include <exception>
#include <iostream>

using namespace std;

extern LogAlerta* Logptr;

Dummy1::Dummy1(void)
{
	Logptr = LogAlerta::getInstance();
}

Dummy1::~Dummy1(void)
{
}

void Dummy1::DummySimple() const 
{
	std::string s("DummySimple");
	try
	{
		// Remove comments and things do not work
/*		int i = 0;
		int j = 0;
		int a = i/j;	*/	
	}
	catch( std::exception& e )
	{
		Logptr->Simple(s);
		cout << e.what() << endl;
	}
}





所以我写了一个简单的控制台应用程序来测试它,在代码中调用它使用线程。

在虚拟类中,如果我删除产生异常的注释,程序崩溃,否则它会起作用:





So I wrote a simple console application to test it, calling it in the code and using threads.
In the dummy class if I remove the comments that generate an exception the program crashes, otherwise it works:

#include "stdafx.h"
#include "LogAlerta.h"
#include "Dummy1.h"
#include <iostream>
#include "process.h"
#include "windows.h"

using namespace std;

LogAlerta* Logptr = NULL;

unsigned int WINAPI Thread1(LPVOID lpParam);
unsigned int WINAPI Thread2(LPVOID lpParam);

unsigned int ctrlId[2];
HANDLE ctrlHandle[2];

void UsingThreads();

HANDLE Mutex = NULL;

int main(int argc, _TCHAR* argv[])
{

	cout << "First test: calling it indirectly--------------------------" << endl;
	Dummy1* _d = new Dummy1();
	_d->DummySimple();
	delete _d;
	cout << "---------" << endl ;

	cout << "Second test: calling it directly--------------------------" << endl;
	Logptr = LogAlerta::getInstance();
	Logptr->getInstance();
	string _s("Straight first time");
	Logptr->Simple( _s );
	Logptr = NULL;
	_s.clear();
	_s.append("Straight second time");
	Logptr = LogAlerta::getInstance();
	Logptr->getInstance();
	Logptr->Simple( _s );
	cout << "---------" << endl ;

	cout << "Third test: calling it indirectly again---------------------" << endl;
	Dummy1* d = new Dummy1();
	d->DummySimple();

	delete d;
	d = NULL;

	cout << "---------" << endl ;
	cout << "Fourth test: calling it indirectly after dummy object is deleted:" << endl;
	d->DummySimple();

	cout << "---------" << endl ;
	cout << "Sixth test: calling it indirectly after Dummy object is deleted and Logptr made NULL" << endl;
	Logptr = NULL;
	d->DummySimple();

	cout << "---------" << endl ;
	_s.clear();
	_s.append("7th: Calling it direcly");
	LogAlerta::getInstance()->Simple(_s);

	cout << "---------" << endl ;
	cout << "---------" << endl ;
	cout << "Test calling it within threads, no syncronizations..." << endl;

	UsingThreads();

	return 0;
}

unsigned int WINAPI Thread1(LPVOID lpParam)
{
	Dummy1* ptr  = (Dummy1*)lpParam;
	cout << "Test within first thread" << endl;

	WaitForSingleObject( Mutex, INFINITE );
	try
	{
		ptr->DummySimple();
	}
	catch( ... )
	{
		cout << "Exception raised on thread 1 " << endl;
	}
	ReleaseMutex( Mutex );

	ExitThread(0);
	return 0;
}

unsigned int WINAPI Thread2(LPVOID lpParam)
{
	Dummy1* ptr  = (Dummy1*)lpParam;
	cout << "Test within second thread" << endl;

	WaitForSingleObject( Mutex, INFINITE );

	try
	{
		ptr->DummySimple();
	}
	catch( ... )
	{
		cout << "Exception raised on thread 2 " << endl;
	}
	ReleaseMutex( Mutex );
	return 0;
}

void UsingThreads()
{

	Dummy1 StaticObject1;
	Dummy1 StaticObject2;

	ctrlHandle[0] =  (HANDLE)_beginthreadex(NULL, 0,Thread1,&StaticObject1,0,&ctrlId[0]);

        ctrlHandle[1] =  (HANDLE)_beginthreadex(NULL, 0,Thread2,&StaticObject2,0,&ctrlId[1]);
	
	// Esperando encerramento das thread
	WaitForMultipleObjects(2,ctrlHandle,false,INFINITE);
	
	// Fechando handles
	CloseHandle(ctrlHandle[1]);
	CloseHandle(ctrlHandle[0]);

}





输出结果为:



The output is:




第二次测试:直接调用它----------------------- ---

LogAlerta已经发起

LogAlerta已经启动

第一次称为Straight的简单方法

LogAlerta已经发起了

LogAlerta已经发起了

简单的方法叫做Straight第二次

---------

第三次测试:再次间接调用它---------------------

LogAlerta已经启动

---------

第四次测试:在删除虚拟对象后间接调用它:

---------

第六次测试:删除Dummy对象后间接调用它并使Logptr成为

NULL

---------

LogAlerta已经发起

简单方法叫做第7名:直接调用

---------

- --------

测试称它为wi细线程,没有同步...

LogAlerta已经启动

LogAlerta已经启动

在第一个线程中测试

测试inPresse qualquer tecla para continuar。 。 。



编辑:上述测试方案与查找问题的根本原因无关。

问题是由异常抛出代码中的某个地方。根据@nv3的建议,我创建了上面简单的测试程序,但它没有给我太多信息。除了它与线程无关。



我正在寻找解决方案目录(一大堆C ++,C#,ASP,SQL,项目,我错过了一个对于问题:有人为这个Logging类创建了一个测试项目。他们甚至创建了一个小的VB 6项目来测试它。这个人创建了这个测试项目作为DLL,但我将它转换为一个简单的控制台应用程序然后它是事实上,我认为如果我在本地调试,我会更容易找到问题,这是我迄今为止开发的100%Windows应用程序和DLL所做的事情。无论如何,在有了看看它的本地应用程序,它很简单:



过去他们创建的缓冲区无法容纳完整的字符,它们不够大。它们有也没有发起任何东西。在我之前有人将所有strcpy和strncat转换为他们的安全格式(追加_s后缀),当我试图运行它们时,旧的b uffers没有使用这些更安全的版本。



之前它是这样的:


Second test: calling it directly--------------------------
LogAlerta was already initiated
LogAlerta was already initiated
Simple method called Straight first time
LogAlerta was already initiated
LogAlerta was already initiated
Simple method called Straight second time
---------
Third test: calling it indirectly again---------------------
LogAlerta was already initiated
---------
Fourth test: calling it indirectly after dummy object is deleted:
---------
Sixth test: calling it indirectly after Dummy object is deleted and Logptr made
NULL
---------
LogAlerta was already initiated
Simple method called 7th: Calling it direcly
---------
---------
Test calling it within threads, no syncronizations...
LogAlerta was already initiated
LogAlerta was already initiated
Test within first thread
Test withinPressione qualquer tecla para continuar. . .

The above test scheme was not really relevant to find the root cause of the issue.
The problem was caused by an exception thrown somewhere in the code. With the suggestion from @nv3 I have created the above simple test program, but it did not give me much information. Except that it was nothing related to threads.

I was looking around the solution directory ( a huge set of C++, C#, ASP, SQL, projects and I missed a single issue that turned to be crucial to the problem: Someone had created a test project for this Logging class. They have even created a small VB 6 project to test it. This person has create this test project as a DLL, but I converted it to a simple console application and then it was a matter of debug it. In fact I think I would have found the problem more easily if I was locally debugging, which is something I have made for 100% of my Windows applications and DLLs I had developed so far. Anyway, after having a local app to look at it, it was rather simple:

In the past they have created buffers that could not hold complete chars, they were not big enough. They have also not initiated anything. Someone before me had converted all strcpy and strncat, to their safe formats ( _s suffix appended ) and when I was trying to run them the old buffers were not working with these safer versions.

Before it was something like:

/* monta o prefixo do registro */
strcpy(stPrefixoMensagem.szPref01, "  - ");



然后,有人将其更新为


And then, someone updated it to

/* monta o prefixo do registro */
strcpy_s(stPrefixoMensagem.szPref01, "  - ");





使用旧编译器(VS 2003)和系统(Windows 2000 Server / IIS 5)没有发生任何事情,但是创建更新时,插入了一个错误。事实上,我仍然不知道我是否会遇到更多这样的错误,事实上我之前遇到过很多错误并纠正过它们。此时系统进入非通常状态,称为记录类。我感谢所有帮助过我的人,为不准确和误解道歉。我肯定会提到nv3已经给了我解决方案的关键部分所以如果nv3发布一个解决方案,我想赞成nv3。再次感谢,我道歉。



With the old compiler ( VS 2003 ) and system ( Windows 2000 Server/IIS 5 ) nothing was happening, but when the update was created a bug was inserted. In fact, I still don't know if I will meet more bugs like this, in fact I had met many before it and corrected them. At this time the system entered a non usual condition which called the logging class. I am thankful to everybody who has helped me with this, apologize for inaccuracies and misconceptions. I would definitely mention that nv3 has given me the crucial part of the solution so should nv3 post a solution to this I would like to credit nv3. Thanks again, and I apologize.

推荐答案

在此处移动我的评论。如果它解决了您的问题,您可以将其标记为解决方案...



您第一次打电话到课堂的时间是什么时候?



也许在加载依赖库之前调用它?

在使用非本机Win32的任何调用开始记录之前,请确保您的应用已完全加载。
Moved my comment here. If it fixes your issue you can mark it as the solution...

When does your first call to the class occur?

Perhaps it is being called before dependent libraries are loaded?
Be sure that your app has fully loaded before you begin logging using any calls that aren't native Win32.


几点想法:

(1)你的getinstance()API不是线程安全的。如果您有2个或更多线程同时调用它,您将获得2个或更多对象。

(2)是否只有1个.obj文件的副本链接到某处?如果.cpp - > .obj文件被链接到2个或更多的地方,你实际上有2个可以调用的代码副本。

(3)return不是函数。如果你维护我的代码库,我会让你摆脱你的返回声明中的parens。即。 返回日志;不是return(log);
Several thoughts:
(1) your getinstance() API is not thread safe. If you have 2 or more threads calling it at the same time, you will get 2 or more objects created.
(2) Is there just 1 copy of the .obj file linked somewhere? If the .cpp -> .obj file is linked into 2 or more places you effectively have 2 copies of the code that can be called.
(3) return is not a function. If you were maintaining my code base I would make you get rid of the parens on your return statement. ie. "return log;" not "return(log);"


这篇关于Singleton C ++类声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆