• 微软原版系统

  • 一键重装系统

  • 纯净系统

  • 在线技术客服

魔法猪系统重装大师 一键在线制作启动 U 盘 PE 系统 用一键重装的魔法拯救失去灵魂的系统
当前位置:首页 > 教程 > 电脑教程

面向对象设计还需要指针么?

时间:2015年04月02日 15:20:15    来源:魔法猪系统重装大师官网    人气:9365

大三那会还在搞单片机和MFC,玩的纯C系的语言,每天和指针打交道,一切皆指针。有一天,听说JAVA里没有了指针,我大惊失色,指针都没了,这语言还能搞啥?

后来,类似C#,JAVA的高级面向对象语言用得多了。反过来思考,高级面向对象语言没有了指针,到底是好事还是坏事?这种区别体现在哪里?本文以C#和C++为例做个对比,JAVA机制和C#类似。与各位共同探讨。

为了简单,我们先定义一个Point类, 只有X,Y 两个变量。看看C++和C#之间的使用区别

1. 指针和引用

C++中,指针和引用的有一定的区别,指针是一个地址,而引用只是别名,引用使用起来要方便得多。因为指针本身是地址,地址当然可以指向任何地方,所以便有了指针的指针,如果再和数组,函数,结构和类联系上,那简直就是考验人的大脑。 C++的引用,定义了就必须在声明时就初始化,而且不能更改。

C#中,只有引用,一切“引用型变量”都是引用。但这个引用和C++中的引用不同,它更像一个“地址”。如果你声明了 Point p, p就是引用。但是这个p可能没有初始化,你也可以在任何时候改变它。

点评: 指针本来是好东西,但它太灵活,搞得太复杂了。反正我现在不大喜欢看*和&这类符号。

2.类的构造

C++中,可以使用两种方式新建一个变量:

(1) Point p, 你就构造了一个Point变量。 它处在栈区。

(2) Point* p=new Point; 它处在堆区:

最牛的在于,Point points[10]; 这样的声明,会直接产生十个Point对象,处于栈区。

也可以这么定义,Point* ps=new Point[10]; 处于堆区。

C#中,只有一种方式创建:Point p=new Point(); 处于堆区。

3. 交换两个对象

大家一定都记得,初学C++时经典的数字交换问题。

C++中, 两种做法: void Swap(int* a,int *b) 或者是 void Swap(int& a,int& b) ,这个没什么好说的

有意思的是,如果交换两个对象呢? 如果是void Swap(Point a, Point b),那你就已经错了,在执行这个函数时,拷贝构造函数会将你的实参分别拷贝到a,b两个对象中,处在堆区。当函数返回时,你除了浪费了一堆时间做无谓的拷贝工作外,对象还是原来的对象。 因此还得是 void Swap(Point* a, Point *b)或者 void Swap(Point& a, Point& b)。

C#中,交换值类型时,可以使用void Swap(ref int a, ref int b), 如果是引用类型,直接使用void Swap(Point a, Point b)就行了。

点评:你会发现,C++中根本就没有值类型和引用类型这回事,加上*,&才是“引用类型”,否则就是值类型,值类型传递,就会调用拷贝构造函数,造成一定的性能损失。而C#不存在这个问题。

4.函数返回值与工厂模式

设计模式的构造模式中,工厂是最常见的,你可以非常方便的写一个C#版本的工厂,但在C++中,怎么实现工厂?

Point GetProduct()
{
Point p; //设置为默认值就可以了。
returnp;
}

调用时 Point *p= &GetProduct();

你觉得这样对么?留给读者讨论。

5.类的组合和对象拷贝

类可以组合,也可以继承。但在C++中,类组合会带来额外的问题,考虑如下的结构:

classPointEx
{
public:
Point A;
Point B;
//其他成员和函数
}

那么你声明一个PointEx pex, 那么A,B两个对象就会被自动创建。如果希望能在构造函数中传递A,B两个类的参数,那必须使用“内部对象构造函数”,具体细节参考普通C++教材。而对象拷贝时,也带来了额外的隐患,默认的拷贝构造函数只能拷贝普通类型,但这些组合的成员,如果直接=的话,那么两个PointEx可能就指向同一个Point A。

在C#中,不存在这样的问题,首先A,B在PointEx的构造中,根本就不会被构造。除非你在PointEx中显式的new出A和B。

除此之外,C++中,子类和父类的构造函数与析构函数的执行顺序相当重要。C#中,析构函数被取消了(因为GC),代之以Dispose模式。

6. 函数指针和委托

首先,函数指针是无敌强大的,因为存储区分为堆,栈,代码区和全局数据区。所以代码区也是能被指针访问的,因此函数指针指向代码区,就能把函数流程指向那个区域。这是函数指针的实质。

C++中:你可以这样定义函数指针 int (*sqrt) (int x); (看着真恶心,你还没见函数指针数组,返回函数指针的函数等等丧心病狂的类型) 。可以这样赋值:

先随便定义一个函数sqrt2, 然后func= sqrt2;就可以了,

C#中:可以这样定义委托 delegate int sqrt(int x); 赋值类似。 当然还有更牛的“事件”,实现了多播委托(我理解就是委托数组)

点评: 委托的最大好处是强类型的,而且看着比函数指针优雅的多,功能也更强。

最近,我认识到了函数式编程的强大。如果能认识到“函数也是变量”,那么就是一个很大的进步。

7. 指针类型转换

先说普通值类型的指针类型转换。

C++中:定义两个指针, int*a 和float*b . 两个是不能直接赋值的。要想赋值,只能采用空指针作为中介:

void*pv= a;
b= static_cast(pv);

如果是对象,而且是有继承关系的,例如Point3D继承于Point,那么从Point*到Point3D*如何转换呢?

C#中,可以采用强制类型转换或as关键字:

Point3D p3d= (Point3D)p2d; 或者是 Point3D p3d= p2d as Point3D;

点评:类型是任何一门语言中都非常复杂的一部分。 C#有强大得多的类型系统和元数据模型,处理起来要更高级一些。 不过,我有个疑问,C++中,什么区域存储一个变量的类型?机器怎么知道某一块区域内的内存是int而不是double? 难道存储在代码区?

8. 类和结构体的区别

C++中,类和结构体本质几乎没有区别,结构体也能定义函数,定义不同的变量成员,只是所有成员都是公开的;而类有访问控制,可以实现继承和派生。

C#中:类是引用类型,结构体是值类型。结构体作为参数或返回值,都需要做拷贝。而且,结构体无法定义除了构造函数之外的成员函数。用结构体,可以获得比类更高的性能。

导致这些区别的原因:内存分配

其实,说这么多,导致C#和C++的 这些区别的本质原因,在于它们内存分配机制的不同。

C++中,不论是对象还是普通的值,如果是通过 Point p这样的语法生成的话,那么就在栈上。一旦函数结束,栈就被回收。只有new关键字生成的对象,才放在堆上。

放在栈上的数据因为随时可能被回收,才需要这么复杂的指针机制。指针的地址问题,这就像装盒子一样,一个盒子不安全,就多套几层盒子,盒子越套越多,搞得越来越复杂。

C#中,有值类型和引用类型的区别。所有的引用类型,都在堆上,回收靠GC处理。普通函数中定义的值类型都在栈上。如果是对象当中的“值”,那当然也定义在堆上。因为堆比栈方便多了,一个地址就可以了。所以使用起来要比在栈上方便的多。

要说性能,当然是栈比堆快,和内存访问的集中性有关,栈的数据基本都在高速缓存当中,命中率极高。但堆却不一定。 正是因为GC的作用,允许引用类型就定义在堆上。当然,我相信对于C#这样的语言,在编译或运行时应该会计算哪些是数据访问热点,从而优化命中率。 C#这类高级语言,是靠一个强大的“运行时”(runtime)和虚拟机来帮助它实现了这类区别。

我现在还不是很清楚,堆和栈的比例是怎么分配的。以前搞单片机的时候,会有一个编译选项选择它们的比例,我一般会把栈内存(heap)拉到90%,我不愿意用堆,因为觉得麻烦。

那么,高级面向对象语言需要指针么?

有了上面的那一段,估计大家都有答案了。因为有了强大的运行时支持,有了垃圾回收器,使得堆的使用率比栈大的多。虽然性能上会有那么一点点损失吧,但带来的确实是代码的简洁,高效,程序员再也不需要玩指针的游戏了。它带来了以下好处:

1. 省略了拷贝构造函数和析构函数

2. 简化了函数的参数和返回值,不需要再去调用拷贝函数了,性能有所提升。

3. 简化了对象组合的复杂性,C++内存管理本来就够复杂了,面向对象会变得更加复杂,稍微不注意就会造成内存泄露和指针悬挂等。而在C#中,你可以放心大胆的使用组合和继承。思路会清晰的多。

4.C#,JAVA代码好看得多,起码没有那些奇怪的符号。

5. 其他我还没有想到的好处。

从这个角度来说,指针的作用已经被“引用”类型代替了。

但是,C++不需要运行时和虚拟机,直接编译为原生代码,性能肯定更好,但确实C++难学。C++大牛肯定能列出一堆C++内存机制的好处,这个见仁见智了。

还有些遗留的问题,当开启C#的unsafe选项后,可以在C#里直接写C++,那么这种内存管理是如何完成的?

面向,对象,设计,还,需要,指针,么,大三,那会,
栏目:电脑教程 阅读:1000 2023/12/27
Win7教程 更多>>
U盘教程 更多>>
Win10教程 更多>>
魔法猪学院 更多>>

Copyright © 2015-2023 魔法猪 魔法猪系统重装大师

本站发布的系统仅为个人学习测试使用,请在下载后24小时内删除,不得用于任何商业用途,否则后果自负,请支持购买微软正版软件。

在线客服 查看微信 返回顶部