引用、指针、抽象类的多态 in C++ and C#

原创 2011年01月12日 11:28:00

一、C#的引用和C++的指针

    C++的对象是“值类型的”(相对于C#引用类型),我们在实例化一个C++对象时有两种方法,第一种方法是:Person person1; 这种方法创建的对象person1和创建普通值类型的变量一样,存放在栈中,代表一个完整的对象本身,这也是C++和C#不同的地方,C#的对象本身只能存放在堆中,但其对象的引用是存在栈中的。第二种方法是:Person* person1 = new Person(); 用这种方法创建了一个指向对象本身的指针,它存放的是对象本身的地址,而这个对象本身是存放在堆中的。

    现在来看看C#。C#的对象是引用类型(暂且这么说,以便区分)。由于C#的对象本身都是存放在堆中的,我们在实例化一个C#对象时,都会用到new来在对象中创建一个对象并且返回一个指向该对象的引用(其实就是指针),如Person person1 = new Person(); 这里的person1就是指向实际对象本身的一个指针,它只是一个地址而已,但是我们在访问或修改该对象本身的时候只需要把person1当做一个实际的对象来操作即可(这一点容易让人想到C++的引用或别名,实质上它更接近于C++的指针),但切记它只是一个地址,并且这个地址的值是可以改变的(从这一点就可以看出它不同于C++的引用,更像指针,因为它指向的对象是可以改变的)。为了搞清楚这个问题,我们来做几个测试程序。

  

这里我们创建了三个Person对象,通过姓名来区分。首先我们让person3和person1指向同一个堆中的对象,即person1的值和person2的值相等,都等于Person("Mkie")这个对象的地址,他们都引用一个相同的对象。然后我们让person1和person2指向同一个对象,即此时的person1的值发生了改变,它存放的是Person("Jack")这个堆中的对象的地址,但是这一步操作并没有改变person3的值,即person3的值依然是之前Person("Mike")这个对象的地址。最后在打印person1和person3的姓名时可以看到结果,分别为"Jack"和"Mike",也就是说最后通过person1引用到的对象是之前Person("Mike")这一步创建的,通过person3引用到的对象是之前Person("Jack")这一步创建的对象。

 

在上面程序的基础上继续考虑,如果我们要交换两个对象引用person1和person2的值,实际上就是交换两个地址的值,这样同样可以实现交换。不过假如我们要在子函数中去交换这个两个对象引用的值,就必须要加上ref关键字,如下

 

这里通过ref关键字,可以把 person1和person2两个对象引用的地址传给子函数,即指向指针的指针,在C#可称为引用的引用。子函数的函数堆栈中保存这两个对象引用的地址,然后通过引用的引用来改变主函数中person1和person2的值,从而达到交换两个对象引用的目的。如果这里不加ref关键字,则子函数堆栈得到的仅是主函数中person1和person2的值,即两个对象的地址,将这两个地址进行交换实际上只是将在子函数堆栈中的两个值交换,并不能改变主函数中person1和person2的值,记住这里我们要改变的只是实际对象引用的值而不是实际对象的值,如果要改变实际对象(堆中)的值,则不需要ref,直接通过传递对象引用,然后再子函数中通过引用来访问对象即可person1.Name=?,这就是引用的作用。

    我们在这里再来把C#和C++做个比较。如果是在C++中交换两个对象的值应该如何操作?

    首先,我们需要为待交换类重载赋值运算符,因为交换对象是需要赋值的。这个重载函数根据具体的类定义来实现。如下:

 

以上是Person类的定义。在主函数中对创建两个person对象,并在一个子函数中交换他们。这里我们可以用两种方法,

方法1:在堆中创建对象,通过改变指向对象的指针的值来交换对象。如下:

 

这里,我们把子函数的参数设置为指向指针的指针,目的是为了在子函数中改变主函数指针的值,即person1和person2的值,通过交换他们的值后,person1实际指向的对象就变成了之前person2指向的对象,而person2现在指向的对象变成了之前person1指向的对象。这样我们再通过对这两个指针进行解引用操作就可以得到交换后的对象。这种方法实际上是没有用到我们之前定义的赋值运算符重载函数的。

方法2:还是在堆中创建对象,这时我们的子函数参数是设置为指向Person对象的指针,我们要做的是在子函数中对指针进行解引用然后调用重载赋值运算符函数对实际的对象进行交换。这是最容易理解的方法,也是最直接的方法。代码如下:

 

这种方法即为我们用得最多的传地址调用函数的方法。

 

以上两种方法都可以实现对象的交换,实质上第一种方法只是交换了指向对象的指针的值,并非真正的对象交换,第二种方法才是将对象本身进行交换。在C++中我们可以如此灵活的去操作对象, 但是在C#中,由于“引用”这一概念的存在,我们在创建一个对象的时候必须要创建一个相应的引用,可是引用却又仅仅是对象的一个地址,它很像指针,但是我们却又不能对它进行解引用操作来表示实际对象的值,只能通过它去调用对象的公有成员,我想这也是C#中不允许重载赋值运算符的原因,因为我们对实际的对象进行赋值,我们能做的只是对对象的引用进行赋值操作。因此,C++在对象的操作比C#要灵活许多。不过C#也有C#的好处,我们有时候要对对象进行改变,不如实例化一个新的对象,旧的对象就不用去管它了,GC会帮我们清理掉。不过我更喜欢指针的灵活,它可以让程序员有种脚踏实地的感觉,额。。。

 

二、抽象类的多态

 

    再来谈谈多态吧。

 

    多态是将父类设置为和一个或多个子类相等的技术。通过多态,我们可以屏蔽子类之间的差异,把子类当做父类来使用,从而可以通过父类来调用子类中重写的方法,以实现通用性。

    抽象类是不能够实例化对象的,无论是在C++、C#还是Java中都是成立的。因为抽象类中含有抽象方法(在C++中叫做纯虚函数),这种方法没有实现代码,因此无法从一个抽象类创建这样一个不完整的对象。

    由于C++和C#在对象引用上的不同(这句话表达的很模糊,有待精炼。。。),具体应用的时候会有一些差异。

    上面几行说到,我们不能从抽象类实例化一个对象,也就是说我们不能用抽象类的对象来完成多态(这句话表达的也不够好)。不过我们可以用抽象类对象的引用或者指针来实现多态。

      还是用刚才那个Person类

 

这里我们将Customer对象的指针向上转型为Person对象的指针,完成了多态。

如果这里我们将main函数改成以下情况

 

这里,我们将Customer对象向上转型为Person对象,这样做是不对的,因为刚才提到抽象类不能创建对象,而这里经过想上转型后,实际上就创建了一个抽象类的对象,所以我们不能这么做。

 

好了,这是C++的做法。再来看看C#是怎么做的。

 

这里我们创建了两个类,ball为抽象类,有一个抽象方法Display(), football类继承与ball类,实现了Display(),这里要注意,在实现Display()时,必须要加上override关键字,这一点不像C++中直接覆盖就可以了。

主函数如下:

 

由于foot1是引用而不是具体的对象,因此我们可以将其向上转型为ball类对象,并实现多态。

以上就是对C++和C#在指针、引用、抽象类的多态方面的一点感想,很乱,想到哪写到哪,就当是笔记了。

今天就写这么多,噢啦~

 

为什么C++中只有指针和引用才能实现多态?

类似代码如下: class A { public:     virtual void Test(){} }; class B:public A { public:    virt...
  • wusecaiyun
  • wusecaiyun
  • 2015年10月05日 23:49
  • 1077

c++多态之抽象类编程

c++之多态 继承 封装
  • SUP_PLAYBOY
  • SUP_PLAYBOY
  • 2016年04月26日 00:07
  • 376

C#基础-----抽象类实现多态

什么是抽象类:光说不做,不能被实例化的类 抽象类的特点: 1,需要用abstract关键字标记 2,抽象方法不能有任何方法实现,所以没有方法体 3,抽象成员必须包含在抽象类中 4,由于抽象成...
  • shang1010
  • shang1010
  • 2015年06月09日 15:12
  • 498

面向对象(多态,抽象类,接口的比较)

(一)面向对象(多态) 多态概述: 多态实质上是同一个对象相应不同消息的能力,你理解不了也可以把它理解为事物存在的多种形态. 当然了能够理解实质当然更好. 多态的前提: 要有继承关系, 要有方法重...
  • tongzhengtong
  • tongzhengtong
  • 2015年09月10日 23:32
  • 1563

抽象类不能定于对象,但可以用抽象的类定义指针!

可以用一个抽象类定义一个指向该类的指针,但是不能定义或声明对象。 这个中原因就是,定义一个类的对象是,编译器在编译时需要了解类的所有信息以便准确的为要实例化的对象分配内存;由此可见,定义一个类对象是...
  • qq_17242957
  • qq_17242957
  • 2016年10月09日 10:07
  • 2557

为什么要使用虚函数和 指针(或是引用)才能实现多态?

首先说说为什么是这样一个题目 最近我在学习《深度探索C++对象模型》这本书,明白了C++对象模型的内存布局。但也恰巧是这个内存布局让我有一次陷入了深深的疑惑之中。先看看我的例子: 注:此例也是引用某位...
  • sangyongjia
  • sangyongjia
  • 2016年03月14日 20:10
  • 1365

指针与引用实现多态

引用是除指针外另一个可以产生多态效果的手段。 #include using namespace std; class A { public:  virtual void print() ...
  • hi_software
  • hi_software
  • 2012年09月13日 12:37
  • 2803

C++中引用(&)的用法和应用实例===引用和多态的关系!!!!!!!!!!!!!!!!!

http://www.cnblogs.com/Mr-xu/archive/2012/08/07/2626973.html C++中引用(&)的用法和应用实例 对...
  • wangyin159
  • wangyin159
  • 2015年08月29日 19:34
  • 1858

C#学习日记23---多态性 之 运算符重载、方法重载、抽象类、虚方法

在C#中多态性的定义是:同一操作作用于不同类的实例,不同的类进行不同的解释,最后产生不同的执行结果。换句话说也就是 一个接口,多个功能。 C# 支持2种形式的多态性: 编译时的多态性、运行时的多态...
  • HC666
  • HC666
  • 2015年10月12日 19:49
  • 1064

C++ 多态 指针转换

class A{ public: A(); virtual ~A();};class B{ public: B(); virtual ~B();};class C : public A...
  • CNHK1225
  • CNHK1225
  • 2015年10月21日 16:03
  • 757
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:引用、指针、抽象类的多态 in C++ and C#
举报原因:
原因补充:

(最多只允许输入30个字)