《大规模C++程序设计》 总结。
对于大中型项目来说,全局变量,全局函数总是在所难免。而且让人又爱又恨。用的好省时省力,用的不好陷阱重重,程序无法维护。
《大规模C++程序设计》书中总结了几种解决方式,这里把他们解决方式和实例代码保留在这里以便以后查用。
1 全局数据
解决方法:
1) 将他们放在一个全局结构中(struct 或 class)
2) 添加静态访问函数
代码示例:
global.h
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
int size;
double scale;
const char* system;
#endif
很明显,对于这样定义的变量在工程中很容易一起名字冲突,而且很难维护。
优化第一步,把它们放入结构体中;
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
struct Global
{
static int s_size;
<pre name="code" class="cpp"> static double s_scale;
static const char* s_system;};#endif
全局名字的冲突概率已经大大的减小,可以使用Global::s_size 来进行访问
但直接访问静态变量会使得程序变得很难维护。例如把s_size 从int型变为double,客户程序将不可避免的受到影响。此时没有对变化进行封装,所以使用这个变量的所有地方都将受到影响。
优化第二步,添加静态访问函数;
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
class Global
{
private:
static int m_size;
static double m_scale;
static const char* m_system;
private:
Global();
public:
static void SetSize(int size) {m_size = size;};
static void SetScale(double scale) {m_scale = scale;};
static void SetSystem(const char* system) {m_system = system;};
static int GetSize(){return m_size;};
static double GetScale(){return m_scale;};
static const char* GetSize(){return m_system;};
};
#endif
2 全局函数
全局函数的主要问题在于作用域内名字的冲突问题,解决的方法还是用一个全局的结构来把它包起来。
示例程序:不好的使用方式
int getMonitor()
void setSystemInfo(int info)
int validatPassword(string password)
解决方法
struct SysUtility
{
int getMonitor()
void setSystemInfo(int info)
int validatPassword(string password)
}
虽然只是加了一层嵌套结构,但在全局范围内名字冲突的可能性还是大大的降低了。
3 枚举类型,typedef,常量类型
设计规则,在.h文件的文件作用域内,尽量避免使用枚举类型,typedef,和常量数据。
示例程序:
color.h 其中定义了一个颜色枚举ORANGE。
enum Color {RED, GREEN, YELLOW, ORANGE, BLACK};
fruit.h 其中定义了一个水果ORANGE。
enum Fruit {APPLE, ORANGE, GRAPE};
在其他文件中如果同时引用 color.h 和 fruit.h ORANGE 则会出现二义性的编译错误。
这只是一个简单的例子,如果在大中型项目中,可能会引起众多的接口修改。
为了解决这个问题,把他们定义在不同的类中会是一个不错的选择。
例如使用时可以考虑采取 Color::ORANGE 和 Fruit::ORANGE的形式。
其他的常量定义也是如此,把它们限制在某个类中或是在某个结构中。
或者可以考虑将所有的 typedef 限定在一个作用域内,这样也可以避免冲突的发生。
#include <iostream>
using namespace std;
class Juice
{
public:
enum Fruit
{
APPLE,
ORANGE,
GRAPE,
};
static const int USE_HOW_MUCH = 1;
typedef struct _ABC
{
int _A;
double _B;
char _C;
} ABC;
};
class Painter
{
public:
enum Color
{
RED,
ORANGE,
BLUE,
};
static const char USE_HOW_MUCH = '5';
typedef struct _ABC
{
char _A;
char _B;
char _C;
} ABC;
};
int main(void)
{
Juice::Fruit st = Juice::ORANGE;
Painter::Color clr = Painter::ORANGE;
Juice::ABC jabc = {2, 3.4, 'z'};
Painter::ABC pabc = {'a', 'b', 'c'};
cout << "Juice::Fruit: " << st << " Juice::USE_HOW_MUCH: "<< Juice::USE_HOW_MUCH << endl;
cout << "Painter::Color: " << clr << " Painter::USE_HOW_MUCH: "<< Painter::USE_HOW_MUCH << endl;
cout << "Juice::ABC { " << jabc._A << ", " << jabc._B << ", " << jabc._C << "}" << endl;
cout << "Painter::ABC { " << pabc._A << ", " << pabc._B << ", " << pabc._C << "}" << endl;
return 0;
}
4 宏预处理
预处理宏可以在一定程度上为编程提供方便,但大规模使用也可能会造成程序难以维护。
设计规则:除非是作为卫哨,否则尽量少使用预处理宏,它们会使得程序的调试变得很困难。
一个简单的BAD例子:
predef.h
#ifndef PREDEF_H
#define PREDEF_H
#define GOOD 10
#endif
#ifndef CUSTOMER_H
#define CUSTOMER_H
class A
{
public:
enum RESULT
{
BAD,
GOOD,
};
int status;
int getResult()
{
status = GOOD;
return status;
}
};
#endif
main.cpp
#include "predef.h"
#include "customer.h"
#include <iostream>
using namespace std;
int main()
{
A a;
cout << "Result=" << a.getResult() << endl;
return 0;
}
程序在箭头处提示了两个编译错误,如果不明情况恐怕很难一下定位出问题的所在,在大中型项目中恐怕需要更多的时间来搞清楚到底发生了什么。
所以慎用 预定义的宏处理。