JAVA转型C++(三):面向对象

一、类

C++中定义类的机制与JAVA很不相同。在C++中定义类的方法是:以关键字class开头,然后是类的名称。类主体用花括号括起来,并以一个分号结尾。
类的定义中需要声明数据成员与数据函数。可以在类定义的外部定义成员函数。要在类的外部定义函数,需要用类名与域解析运算符(::)来对其进行限定,以便让编译器知道该类定义属于哪个类。
类中的数据成员可以使基本数据类型,也可以是其他对象类型。我们可以使用面向对象编程中的聚合体来模拟这种对象之间的关系。

数据成员的访问以及成员函数的调用在C++中均与JAVA类似,不再赘述。需要注意的是C++中的对象实例化与基本数据类型的声明一样,都是“类名 对象名;”的形式。在C++中的new关键字是动态分配内存,返回的是新建对象的地址,而且分配的内存都是要靠delete()函数回收的,否则会造成内存泄露。
到目前为止,无论何时定义一个变量,C++都会为其分配所需的内存。常规的定义方式中,当创建变量的函数结束时,C++会释放这部分内存。这部分用于局部变量的内存成为栈。但是用new关键字(以及C语言中malloc关键字)创建的内存是一种与程序中的函数保持独立的内存。这部分内存由程序员负责分配与释放。它们统称为堆(或自由存储区)。
new运算符在堆中分配内存,然后返回其地址。如以下代码:

int* pHeap = new int;
int* pHeap2 = new int(10);//为刚开辟的空间赋值为10
int* pHeap3 = intOnHeap();
int* intOnHeap()
{
int* pTemp = new int(20);//这里声明的int*是堆内存,因此可以作为指针返回。
return pTemp;
}

与栈中局部变量的存储不同,堆中分配的内存必须显式地释放。当不再使用已经用new分配过的内存时,应当使用delete将其释放。如以下代码所示:

delete pHeap;

但是要注意的是,delete语句仅仅释放掉了pHeap所指向的内存,而pHeap本身依然指向那块内存。这个指针变成了野指针。为了防止对野指针解引用出错,我们要对野指针重新赋值:

pHeap = 0;

如果new的内存没有及时delete,会造成内存泄露。

二、几种特殊的函数

1.构造函数
与JAVA有所不同的是,C++类中的函数定义中有一种叫做成员初始式的技巧:当有多个数据成员需要根据传来的参数赋值或初始化时,可以利用以下技术。例如:

#include <iostrea>
using namespace std;
class Critter
{
public:
Critter(int hunger = 0);
private:
int m_Hunger;
};
Critter::Critter(int hunger):m_Hunger(hunger){}

构造函数不需要显式调用,然而,在实例化新对象时,会自动调用构造函数。

2.析构函数
与构造函数对应的,还有一个在对象销毁之前被调用的函数,叫做析构函数。它主要用来执行必要的清理操作,如对堆中内存的清理。析构函数也有默认的空析构函数。
析构函数由前置的~波浪号和类名组成。并没有参数或返回值。例如:

Critter::~Critter()
{
......
}


3.拷贝构造函数
对象有时候是被自动复制的。自动复制发生在:通过值传递给函数、从函数返回、通过初始式初始化另一个对象、作为唯一实参传递给对象的构造函数。
默认的拷贝构造函数只是简单地将每个数据成员的值赋值给新对象中的同名数据成员,即按逐项进行复制。这会导致新的对象的某个堆内存中的成员变量指向堆中的同一个字符串。我们叫它浅拷贝。若要达到深拷贝的效果,必须在拷贝构造函数中创建新的内存地址。如下例所示:

Critter::Critter(const Critter& c)
{
m_pName = new string(*(c.m_pName));
}


4.内联函数
调用内联函数会有较小的性能代价,因为函数内联让编译器将函数复制到其调用处。每次调用函数时,程序的控制权不必跳转到不同的位置。
要将一个函数标记为内联函数,只需将inline置于函数定义之前。而函数生命中不适用inline。例如:

inline int radation(int health){cout << health;}
inline int Critter::Bored(int bored){cout <<bored;}

将函数标记为内联函数时,编译器要决定是否将该函数内联。如果编译器认为内联不会提升效率,则不会对其进行内联。

5.友元函数
类的友元函数可以访问其任意成员而又不属于类的成员函数。指定函数为某个类的友元函数的方法是,在类定义中列出前面带有关键字friend的函数原型。例如:

class Critter
{
friend void Peek();
private:
int a;
}
void Peek()
{
cout << Critter.a;
}


6.运算符重载
运算符重载可以为内置运算符定义新的意义,以便将它们用于自定义类型。例如,可以重载*运算符使其用在两个3D矩阵时计算它们的矩阵乘积。
重载运算符的方法是,定义一个名为operatorX的函数,其中X是需要重载的运算符。例如:

ostream& operator<< (ostream& os, const Critter& aCritter)
{
os << aCritter.m_Name;
return os;
}

三、继承与多态
C++中的继承,例如从Enemy类派生出Boss类,方法如下:

class Boss : public Enemy

实际上,基类的一些成员函数并没有被派生类继承。它们是:构造函数、拷贝构造函数、析构函数、重载的赋值运算符。在派生类中必须自己编写这些函数。而另外要知道的是,在实例化派生类的对象时,自动调用基类的构造函数,但也可以从派生类的构造函数中显式地调用基类构造函数。析构函数也与其类似。

当从一个类派生新类时,可以控制派生类访问基类成员的权限。例如上述代码中在Enemy前有一个public,说明成基类中的共有成员成为派生类中的共有成员,保护成员成为保护成员,私有成员成为私有成员。

对于任何继承的积累成员函数,如果期望在派生类中对其进行重写,则应当使用关键字virtual将其声明为虚函数。在将一个成员函数声明为虚函数时,这位成员的重写版本提供了一种方法,使得成员函数能如预期那样用于对象的指针与引用。而在成员定义中不出现virtual关键字。一旦成员函数被声明为虚函数,它在任何派生类中都是虚函数。在重写一个虚成员函数时,不必再声明中使用关键字virtual。但提倡应当使用该关键字提醒我们这是一个虚函数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值