65.9K
CodeProject 正在变化。 阅读更多。
Home

货币

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.11/5 (7投票s)

2004年4月15日

5分钟阅读

viewsIcon

44102

downloadIcon

508

一套货币操作类

引言

在开发某种类型的会计应用程序时,我遇到了支持多种货币并进行各种计算的问题。在查阅 MSDN 后,我了解到 Windows API 本身、MFC(或任何其他类库)都不提供对货币管理的适当支持——这就是开发我自己的类集的原因。

背景

作为应用程序骨干的数据库,以及许多其他表,包含这两张表(我们不关心产品类别)

Database Structure

问题在于:我们有“供应商”及其“产品”。每个产品都有其“进货价”、“管理费”和“零售价”。后者(即“零售价”)独立于供应商的“货币 ID”,而所有其余与货币相关的字段都依赖于它。随着程序的运行,需要执行各种计算(计算收入、账单等),这就是问题所在。

最初的想法(实际上不是最好的想法)是这样的:

  • 声明几个变量(每个货币一个)……
...
double m_dUSD, m_dEuro, m_dRouble;
...
  • ……然后在各地有以下几乎相同的代码段
switch(GetDocument()->GetSupplierCurrencyID(m_pProducts->m_lSupplier))
{
    case 1: // USD
        m_dUSD += m_pProducts->m_dOverheads;
        break;
    case 2: // Euro
        m_dEuro += m_pProducts->m_dOverheads;
        break;    
   case 3: // Roubles 
        m_dRouble += m_pProducts->m_dOverheads;
        break; 
    default:
        break;
} // switch

这种方法有很多缺点,这是显而易见的。首先,它缺乏灵活性(如果我想添加一种新的货币,我必须查看所有源代码,才能在每个 switch 块中添加一个新的 case)。其次,管理所有与货币相关的事宜(格式化、执行计算等)的负担落在了程序员的肩上。这四个类似乎解决了这个问题(至少在我的情况下)。下面是它们。

参考

CCurrency

嗯,名字不言自明——一个功能强大的基本货币类。

CCurrency();
CCurrency(CYID cyID, double dValue);
CCurrency(CYID cyID,  LPCTSTR szCurrencyName, 
 LPCTSTR szCurrencySign, LPCTSTR szStockName, double dValue);
CCurrency(const CCurrency& rCy);

各种构造函数。CYID(`unsigned long` 的 `typedef`)是货币标识符。在程序中使用不冲突的标识符会更好。`dValue` 是您的 Currency 对象的初始数值。`szCurrencyName` 是对象所代表货币的易读名称(例如,“美元”)。`szCurrencySign` 是货币的符号(例如,“$”)。`szStockName` 是货币的国际名称(我猜是“USD”)。因此,构造可以如下所示:

const CYID CY_USD = 1;
...
CCurrency cyUSD(); // Empty object - I wonder why you need this kind
CCurrency cyUSD(CYID_USD, 1437.6); //  This object has $ 1437.6 inside
CCurrency cyUSD(CYID_USD, _T("US Dollars"), 
 _T("$"), _T("USD"), 52147.25); // This is a fully-defined 
 // object with 52147.25 USD inside
void Reset();

重置 `Currency` 对象(即,将 `m_dValue` 和 `m_lSummands` 设置为 0)。

IGETSET(m_cyID, CurrencyID, CYID);
IGETSET(m_dValue, Value, double);
IGETSET(m_lSummands, Summands, long);
IGET(m_szName, CurrencyName, LPCTSTR);
IGET(m_szSign, CurrencySign, LPCTSTR);
IGET(m_szStock, CurrencyStockName, LPCTSTR);
CCurrency GetCurrency(CYID cyID);

我太懒了,不想写合适的访问器/修改器,所以这是一个快速而粗糙的解决方案——但尽管如此,它仍然完美地工作。请参阅 `macros.h`。

const CCurrency& operator = (const CCurrency& rCy);
CCurrency& operator + (const CCurrency& rCy);
CCurrency& operator - (const CCurrency& rCy);
CCurrency& operator += (const CCurrency& rCy);
CCurrency& operator -= (const CCurrency& rCy);
CCurrency& operator + (double dValue);
CCurrency& operator - (double dValue);
CCurrency& operator * (double dValue);
CCurrency& operator / (double dValue);
CCurrency operator + (const CCurrency& rCyA, const CCurrency& rCyB);
CCurrency operator - (const CCurrency& rCyA, const CCurrency& rCyB);
CCurrency operator * (const CCurrency& rCyA, double dValue);
CCurrency operator / (const CCurrency& rCyA, double dValue);
double Average();

`CCurrency` 对象上的算术运算(出乎意料!)。

CCurrencyManager

这实际上是 `CCurrency` 对象的容器,具有一些附加功能。

BOOL Register(const CCurrency& rCy);
在管理器中注册 `rCy`。Currency Manager 使用 `std::map`,因此会插入一个副本。
LPCTSTR GetCurrencyName(CYID cyID);
LPCTSTR GetCurrencySign(CYID cyID);
LPCTSTR GetCurrencyStockName(CYID cyID);
double GetCurrencyValue(CYID cyID);
long GetCurrencySummands(CYID cyID);
仅供参考——查看管理器中注册的货币的属性。
LPCTSTR GetSummandsFormat() 
LPCTSTR GetValueFormat() 
LPCTSTR GetAverageFormat()
void SetSummandsFormat(LPCTSTR szSummandsFormat);
void SetValueFormat(LPCTSTR szValueFormat);
void SetAverageFormat(LPCTSTR szAverageFormat);

这六个用于处理格式字符串。它们是普通的 C 风格格式字符串(记住或参考 `printf()` 文档)。唯一要说的是,“Summands Format”(因子格式)需要适合格式化 `long` 值的格式字符串,而另外两个字符串(“Average Format”(平均值格式)和“Value Format”(值格式))需要适合 `double` 的格式字符串。例如:

CCurrencyManager cmgr;
...
cmgr.SetSummandsFormat(_T("%d"));
cmgr.SetAverageFormat(_T("%.2f"));
cmgr.SetValueFormat(_T("%f"));
LPSTR Format(LPSTR szBuffer, LPCTSTR szFormatString, 
  CCurrency &rCy);
LPSTR Format(LPSTR szBuffer, LPCTSTR szFormatString, CYID cyID);
LPSTR Format(LPSTR szBuffer, LPCTSTR szFormatString, 
  CYID cyID, double dValue);

三种格式化函数。与 `sprintf()` 一样,它需要一个输出缓冲区(此处为 `szBuffer`)、一个格式字符串(`szFormatString`)和格式化时使用的数据。第一个版本需要一个 `CCurrency` 对象,其字段将在格式化时使用。第二个版本只需要“Currency Identifier”(货币标识符)。它会检查管理器中是否注册了此类货币,如果找到,则使用其字段(包括“Value”——参见下一个函数)。否则,它返回一个空的输出缓冲区。最后一个版本与前一个版本几乎做同样的事情,但它使用用户提供的 `dValue` 而不是在 `Currency` 对象中找到的值。有特定的格式标志,它们如下:

标志 含义
%name 货币名称(参见 `CCurrency` 构造函数中的 `szCurrencyName`)。
%sign 货币符号
%stock 货币的国际名称
%smnd Currency 对象中的因子数量
%val Currency 对象的值
%avg Currency 对象的平均值

因此,代码片段可以如下所示:
 const CYID CYID_USD = 1;
 ...
 CCurrencyManager cmgr;
 cmgr.Register(CCurrency(CYID_USD, _T("US Dollars"), 
 _T("$"), _T("USD"), 52147.25));
 cmgr.SetAverageFormat(_T("%d"));
 cmgr.SetSummandsFormat(_T(".2f"));
 cmgr.SetValueFormat(_T("%.2f"));
 ...
 TCHAR szBuffer[250] =   { 0 };
 cmgr.Format(szBuffer, _T("The value of %name is %val. "
  "Average value of %stock %val is %avg  "
  "(totalling %smnd summands)"), CYID_USD);
const CCurrencyManager& operator = (const CCurrencyManager& rCm);
CCurrencyManager& operator + (const CCurrencyManager& rCm);
CCurrencyManager& operator - (const CCurrencyManager& rCm);
CCurrencyManager& operator += (const CCurrencyManager& rCm);
CCurrencyManager& operator -= (const CCurrencyManager& rCm);
CCurrencyManager& operator + (const CCurrency& rCy);
CCurrencyManager& operator - (const CCurrency& rCy);
CCurrencyManager& operator += (const CCurrency& rCy);
CCurrencyManager& operator -= (const CCurrency& rCy);

像往常一样——算术运算。请注意,如果您添加或减去一个未在目标管理器中注册的货币,则不会注册新货币,并且 Currency Manager 的状态将保持不变。

CCurrencyExchange

您很少需要直接使用此类。它包含执行两种货币(称为“主要”和“次要”)之间转换所需的信息。用于从“主要”转换为“次要”的汇率称为“直接汇率”。用于从“次要”转换为“主要”的汇率称为“反向汇率”。

CCurrencyExchange();
CCurrencyExchange(CYID cyPrimary, CYID cySecondary, 
  double dDirect, double dReverse);
使用给定的特性构造 `CCurrencyExchange` 对象。
IGETSET(m_cyPrimary, PrimaryCurrencyID, CYID);
IGETSET(m_cySecondary, SecondaryCurrencyID, CYID);
IGETSET(m_dDirect, DirectExchangeRate, double);
IGETSET(m_dReverse, ReverseExchangeRate, double);

访问器/修改器——仅此而已。

CCurrencyExchangeManager

它与 `CCurrencyManager` 几乎做同样的事情——管理货币兑换操作。

BOOL Register(CCurrencyExchange& rCx);
在管理器中注册 Currency Exchange 对象。
double GetExchangeRate(CYID cySrc, CYID cyDest);
BOOL SetExchangeRate(CYID cySrc, CYID cyDest, double dRate);
返回或修改定义的汇率(如果具有给定 ID 的 Currency Exchange 对象已在 Currency Exchange Manager 中注册)。
BOOL Exchange(CCurrency& rCy, CYID cyDest);
BOOL Exchange(CCurrency& rCySrc, CCurrency& rCyDest);
两种 `Exchange()` 方法。第一个方法将 `rCy.GetCurrencyID()` 转换为 `cyDest`,并将结果放入 `rCy`,从而改变其 Currency ID 成员变量。请注意,它不会更改任何字符串成员变量。第二个方法将 `rCySrc.GetCurrencyID()` 转换为 `rCyDest.GetCurrencyID()`,并将结果放入 `rCyDest`。所有兑换都将在 Currency Exchange Manager 中注册了适当的 Currency Exchange 对象后进行。例如:
const CYID CYID_USD = 1;
const CYID CYID_EURO = 2;
...
CCurrencyManager cmgr;
cmgr.Register(CCurrency(CYID_USD, _T("US Dollars"), 
 _T("$"), _T("USD"), 52147.25));
cmgr.Register(CCurrency(CYID_EURO, _T("Euro"), _T("€"), 
  _T("EUR"), 87223.2));
 
CCurrency cyEuro(CYID_EURO, _T("Euro"), _T("€"), _T("EUR"), 87223.2);
 
CCurrencyExchangeManager cxmgr;
cxmgr.Register(CCurrencyExchange(1, 2, 1.27, 0.85)); 
cxmgr.Exchange(cmgr.GetCurrency(CYID_USD),  cyEuro);
... TCHAR szBuffer[250] = { 0 };
// Do    not forget terminator
cmgr.Format(szBuffer, _T("The value of %name is %val."), cyEuro);

反馈

这个集合最初是为我个人使用的,所以可能不完全符合您的需求。如果您喜欢这个东西,请投票。如果您有任何问题——请发送邮件或在此处留言。

历史

  • 2004 年 4 月 15 日 - 初始发布
© . All rights reserved.