货币






4.11/5 (7投票s)
2004年4月15日
5分钟阅读

44102

508
一套货币操作类
引言
在开发某种类型的会计应用程序时,我遇到了支持多种货币并进行各种计算的问题。在查阅 MSDN 后,我了解到 Windows API 本身、MFC(或任何其他类库)都不提供对货币管理的适当支持——这就是开发我自己的类集的原因。
背景
作为应用程序骨干的数据库,以及许多其他表,包含这两张表(我们不关心产品类别)
问题在于:我们有“供应商”及其“产品”。每个产品都有其“进货价”、“管理费”和“零售价”。后者(即“零售价”)独立于供应商的“货币 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 insidevoid 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 日 - 初始发布