你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:技术专栏 / C专栏
实例解析C++/CLI中的继承与枚举(1)
 

本文中,将要介绍与继承相关的C++/CLI主题,并以现实生活中银行交易的三种形式:存款、取款、转账,来说明类的继承体系,且以一种新的枚举形式来实现。

  枚举器

  请看例1中声明的类型,它存在于其自身的源文件中,并编译为一个只包含此类型的程序集:

  例1

public enum class TransactionType :
unsigned char {Deposit, Withdrawal, Transfer};


  与想像的一样,枚举器中的DepositWithdrawalTransfer分别代表012的常量值,但有三个方面却让这个enum类型与标准C++enum类型(也就是"本地enum")大不相同。

  ·enum类只用于取代enum。这使TransactionType成为了一个CLI enum。(也允许enum结构,其与enum类等价。)

  ·此类型的可访问性为public,以使其可从父类程序集外可见。(在C++/CLI中,一个本地enum类型也能有一个访问限定符。)

  ·enum类有一个显式的基本类型限定符:在本例中为unsigned char。(在C++/CLI中,一个本地enum也能有一个基本类型。)默认情况下,基本类型为int。基本类型也能为bool或除wchar_t之外的任意整形。(如果指定bool为基本类型,枚举器必须显式地进行初始化,因为没有默认的初始值。)

  支持这个新语法的原因是CLI enum遵从CLS标准,而本地enum却不遵从。

  CLI enum与本地enum间最大的区别在于构成方式上,枚举名的作用范围由它的父类enum类型来限定。另外,标准C++中定义的整数提升,并不适用于CLI enum

  与本地enum类似,一个CLI enum也能被定义在一个类中,在这种情况下,就不允许使用访问限定符了,因为嵌套类型的可见性,已被其嵌入到的类型可见性所取代。

  交易的抽象基类

  交易类型的继承体系在基类Transaction中,默认从System::Object继承,见例2

  例2

using namespace System;
using namespace System::Threading;

/*1*/
public ref class Transaction abstract
{
 TransactionType typeOfTransaction;
 /*2*/ DateTime dateTimeOfTransaction;
 public:
 /*3a*/ property TransactionType TypeOfTransaction
 {
  TransactionType get() { return typeOfTransaction; }

 private:
  void set(TransactionType value) { typeOfTransaction = value; }
}

/*3b*/ property DateTime DateTimeOfTransaction
{
 DateTime get() { return dateTimeOfTransaction; }
 private:
  void set(DateTime value) { dateTimeOfTransaction = value; }
}

/*4*/ virtual void PostTransaction() abstract;

protected:
/*5*/ Transaction(TransactionType transType)
{
 /*6*/ Thread::Sleep((gcnew Random)->Next(1000,2001));
 /*7*/ TypeOfTransaction = transType;
 /*8*/ DateTimeOfTransaction = DateTime::Now;
}
};


  在标号1中,这个类被标为abstract(抽象类),这意味着它不能被直接实例化。(抽象不是一个关键字,仅仅在此上下文中作了保留。)这个abstract修饰词可用于定义一个抽象类,而无须显式地声明一个或多个成员函数为纯虚类型。

  在类的私有数据成员部分,一个Transaction包含了一个交易类型及一个时间日期戳,两者都由定义在标号3a3b中的属性来访问。在标号2中使用的CLI库值类型System::DateTime允许用一个即时变量显示出当天的日期与时间。请注意,两个属性是怎样拥有公有get方法与私有set方法的。(这是基于新的CLI标准,并且现在已与CLS兼容了。)

  标号4要求每个具体的交易类型都有公共的成员函数PostTransaction,在此的abstract函数修饰符等同于标准C++语法中的纯虚函数,一个抽象(abstract)函数必须显式地声明为virtual

  由于构造函数只应从继承类中调用,所以定义在标号5中的构造函数为protected,但它需做的事情却非常简单:设置新的交易类型为传递进来的类型,并通过调用公有属性DateTime::Nowget方法把时间日期戳设置为当前时间。有关传递进来的交易类型,应为一个值类型,而不允许为nullptr,由于CLI enum的强类型检查,编译器只允许同类型的枚举器被传递,或者同类型的实例,当然了,其也只能被同类型的枚举器所初始化。

  通常地,构造函数必须尽快执行完,在此,为从测试程序中得出更多的结果,所以在程序中安置了一个延迟方法,因此时间日期戳在每次交易时都会改变,见标号6,构造函数会在初始化数据成员之前,随机休眠一段时间。由于每个程序至少都会有一个执行线程,而此线程的有关特征可通过sealed System::Threading::Thread引用类来设置或获取,Thread::Sleep函数则把当前执行线程挂起指定的毫秒数。

  为使挂起的时间有所变化,使用了System::Random引用类来生成一系列的伪随机数,标号6中重载的Next函数则获取了一个"大于等于1000,小于2001"的数,也就是一至两秒钟的延迟。

存款、取款、转账类

  例3定义了存款类,为什么这个类为sealed呢?如果还没有认真考虑过它是否足够"健壮"以可作一个基类,那么还是让它不可以继承吧。 3

using namespace System;

/*1*/
public ref class Deposit sealed : Transaction
{
 /*2*/ Decimal amount;
 int toAccount;
 public:
  /*3a*/ Deposit(double amount, int toAccount) :
  Transaction(TransactionType::Deposit)
  {
   DepositAmount = Decimal(amount);
   DepositToAccount = toAccount;
  }

  /*3b*/ Deposit(Decimal amount, int toAccount) :
  Transaction(TransactionType::Deposit)
  {
   DepositAmount = amount;
   DepositToAccount = toAccount;
  }
  property Decimal DepositAmount
  {
   Decimal get() { return amount; };
   private:
    void set(Decimal value) { amount = value; }
  }
  property int DepositToAccount
  {
   int get() { return toAccount; };
   private:
    void set(int value) { toAccount = value; }
  }

  /*4*/ void PostTransaction()
  {
   Console::WriteLine("{0} -- {1}", DateTimeOfTransaction, this);
  }
  virtual String^ ToString() override
  {
   /*5*/ return String::Format(" Dep: {0,10:0.00} {1,10}",DepositAmount, DepositToAccount);
  }
};


  CLI只支持单一继承,因此,值类和引用类只能有一个直接的基类,默认情况下为System::Object。在标号1中,Deposit直接继承自Transaction,请注意没有public访问限定符,CLI只支持公有(public)继承,所以在此也可写为": public Transaction",但这是多余的。(对本地类而言,当继承的类型为结构struct时,默认为公有继承;当继承的类型为类class时,默认为私有继承。)

  别忘了,CLI库支持一种非常适合金融计算的类型--System::Decimal,可在标号2中用它来表示存款额。

  为了方便,提供了两个构造函数:一个接受表示为Decimal的数额,而另一个接受表示为double的数额。请注意,在两个构造函数的定义中,是怎样使用CLI enum作用域符来访问枚举器TransactionTypeDeposit的。

  为完成抽象基类,需提供标号4中的PostTransaction的实现,DateTime是一个值类型,因此当它的一个实例被传递进来时,它被装箱以匹配WriteLine所期望的Object^,而this表达式类型为Deposit^,其也继承自Object^。在这两种情况中,继承层次会一直往下,直到抵达并调用对应的ToString函数。

  也能把函数PostTransaction声明为sealed,这样它就不能被覆盖了,然而,如果父类本身已经为sealed,那么函数永远也不可能被覆盖。

标号5中的格式指定符{0,10:0.00},表明在10个打印位宽度中右对齐数额,并四舍五入到小数点后两位,且至少在小数点前有一位数。

  Deposit类型直接依赖于TransactionTransactionType类型,所以在Deposit的编译期间,必须确保可访问到这两者的程序集。但是,编译器可能会发出一个警告,表示TransactionType已经被引入了两次,一次是直接,而另一次是间接地通过Transaction,在此,可安全地忽略此警告信息。

  Withdrawal类定义在例4中,而Transfer类定义在例5中。

  例4

using namespace System;

public ref class Withdrawal sealed : Transaction
{
 Decimal amount;
 int fromAccount;
 public:
  Withdrawal(double amount, int fromAccount) : Transaction(TransactionType::Withdrawal)
  {
   WithdrawalAmount = Decimal(amount);
   WithdrawalFromAccount = fromAccount;
  }
  Withdrawal(Decimal amount, int fromAccount) : Transaction(TransactionType::Withdrawal)
  {
   WithdrawalAmount = amount;
   WithdrawalFromAccount = fromAccount;
  }
  property Decimal WithdrawalAmount
  {
   Decimal get() { return amount; };
   private:
    void set(Decimal value) { amount = value; };
  }
  property int WithdrawalFromAccount
  {
   int get() { return fromAccount; };
   private:
    void set(int value) { fromAccount = value; };
  }
  void PostTransaction()
  {
   Console::WriteLine("{0} -- {1}", DateTimeOfTransaction, this);
  }
  virtual String^ ToString() override
  {
   return String::Format("With: {0,10:0.00} {1,10}",
   WithdrawalAmount, WithdrawalFromAccount);
  }
};


  例5

using namespace System;

public ref class Transfer sealed : Transaction
{
 Decimal amount;
 int fromAccount;
 int toAccount;
 public:
  Transfer(double amount, int fromAccount, int toAccount): Transaction(TransactionType::Transfer)
  {
   TransferAmount = Decimal(amount);
   TransferFromAccount = fromAccount;
   TransferToAccount = toAccount;
  }
  Transfer(Decimal amount, int fromAccount, int toAccount): Transaction(TransactionType::Transfer)
  {
   TransferAmount = amount;
   TransferFromAccount = fromAccount;
   TransferToAccount = toAccount;
  }
  property Decimal TransferAmount
  {
   Decimal get() { return amount; };
   private:
    void set(Decimal value) { amount = value; };
  }
  property int TransferFromAccount
  {
   int get() { return fromAccount; };
   private:
    void set(int value) { fromAccount = value; };
  }
  property int TransferToAccount
  {
   int get() { return toAccount; };
   private:
    void set(int value) { toAccount = value; };
  }
  void Transfer::PostTransaction()
  {
   Console::WriteLine("{0} -- {1}", DateTimeOfTransaction, this);
  }
  virtual String^ ToString() override
  {
   return String::Format("Xfer: {0,10:0.00} {1,10} {2,10}",
   TransferAmount, TransferToAccount, TransferFromAccount);
  }
};


  虽然三个PostTransaction的实现是同样的,但在真实的交易处理系统中,这是不可能发生的。

                                                             (Alex 转自天极网)

  推荐精品文章

·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 
·2023年7月目录
·2023年6月目录 
·2023年5月目录
·2023年4月目录 
·2023年3月目录 
·2023年2月目录 
·2023年1月目录 

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089