教家里娃儿学习C++语言时,为了讲解C++的封装、继承、多态性等,写了个简单的主程序框架类。应用程序只要从这个主程序框架类派生出子类,实现其run(...)方法,然后定义一个子类实例即可。
抽象应用程序类AbstractApplication类的代码如下:
// absapp.h
#ifndef __ABSAPP_H
#define __ABSAPP_H
/**
* AbstractApplication 应用程序的抽象框架类。
* 应用程序主类应该从此类派生,override其纯虚函数run,随后定义一个应用程序主类的全局对象实例即可。
* 应用程序的project中应当包含与本类配套的main.cpp/h(用于控制台应用)或winmain.cpp/h(用于Windows GUI应用)。
* 若要获得命令行参数,则应用程序还应#include "main.h"或#include "winmain.h"。
*/
class AbstractApplication
{
public:
AbstractApplication() { instance = this; }
virtual ~AbstractApplication() {}
public:
static AbstractApplication *getInstance() { return instance; }
static int start(void *params);
protected:
virtual int run(void *params) = 0;
static AbstractApplication *instance;
};
#endif /* __ABSAPP_H */
// absapp.cpp
#include "absapp.h"
AbstractApplication *AbstractApplication::instance;
int AbstractApplication::start(void *params)
{
instance->run(params);
return 0;
}
控制台程序的main()函数实现代码如下:
// main.h
#ifndef __MAIN_H
#define __MAIN_H
typedef struct __tagMainArgs
{
int argc;
char **argv;
} MainArgs;
#endif /* __MAIN_H */
// main.cpp
#include "absapp.h"
#include "main.h"
int main(int argc, char *argv[])
{
MainArgs args;
args.argc = argc;
args.argv = argv;
return AbstractApplication::start(&args);
}
Windows GUI程序的WinMain函数实现代码如下:
// winmain.h
#ifndef __WINMAIN_H
#define __WINMAIN_H
#include <windows.h>
typedef struct __tagWinMainArgs
{
HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
} WinMainArgs;
#endif /* __WINMAIN_H */
// winmain.cpp
#include "absapp.h"
#include "winmain.h"
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
WinMainArgs args;
args.hInstance = hThisInstance;
args.hPrevInstance = hPrevInstance;
args.lpCmdLine = lpszArgument;
args.nCmdShow = nCmdShow;
return AbstractApplication::start(&args);
}
上述代码可以用编译器打包,做成静态链接的库文件,供应用程序使用。
测试代码如下:
// testCaseApp.cpp
#include <iostream>
#include <absapp.h>
using namespace std;
class TestCaseApp : public AbstractApplication
{
public:
TestCaseApp() {}
virtual ~TestCaseApp() {}
virtual int run(void *params) override
{
cout << "Hello, no-main Application Framework." << endl;
return 0;
}
private:
};
TestCaseApp testCaseApp;
这个程序在运行时,运行环境会首先创建TestCaseApp类的全局实例testCaseApp对象。在创建这个对象实例时,AbstractApplication的构造函数首先被调用,会将静态成员AbstractApplication::instance初始化为this。由于这时是在创建testCaseApp对象,因此此时的this指针代表的是testCaseApp对象,因此AbstractApplication::instance也就指向了testCaseApp对象。当main()函数能够运行时,testCaseApp对象已经完成创建。随后main函数调用AbstractApplication::start(...),而AbstractApplication::start(...)调用了instance->run(params)。由于这时instance已经指向了testCaseApp,因此instance->run(...)调用的就是testCaseApp对象的run(...)成员函数。
从上述测试代码可以看出,在应用程序看来,这个程序是没有main函数的,只有一个全局对象。这个例子使用了C++的封装、继承、多态等特性解决了C++一直被人诟病的未能摆脱全局的main函数的毛病,其实就是把main藏起来了而已。但是,没有了全局函数,却多了个全局变量,其实也没好到哪儿。其实任何一种语言,总归会需要一个全局的入口点的。就连号称OO做得好的Java,同样也需要在主类中定义一个静态的main函数。过度追求“无全局数据”没啥意思。