声明与定义
在C++中【声明】(declaration)与【定义】(definition)是比较重要的两个概念,只有声明没有定义的类和函数是不可以使用的。【声明】只是告诉编译器有这么一个东西存在,但是没有给他分配内存。所以你即没有给他分配内存,又去使用他,那么就会报错误。
变量是一个比较特殊的存在。在Linux中,用的是g++编译器,他可能会自动给变量的【声明】自动分配内存,使用时就不会报错。但是在Windows,尤其是用Visual Studio编程的同学,用的是微软自家的编译器。这个编译器比较严格,不会给变量的【声明】自动分配内存。所以只要养成 【声明】了就一定要【定义】 的好习惯,那么不管在什么平台编程都不会出错。
到后面这个两个概念也会反复提到,所以别看这两个概念简单就忽视了。
int a = 10; //变量的声明和定义
int b; //变量的声明
b = 10; //变量的定义
void fun(); //函数的声明
void fun(){...} //函数的定义
class A; //类的声明
class A {...}; //类的定义
面向对象编程(OOP)
如果之前学过C语言的同学,培养的编程思维应该是【面向过程】编程。这样的逻辑思维在刷题和解决问题方面没有什么问题,但是在大工程面前就会显得比较吃力。于是有人就提出了另外一种很好的编程方案,那就是【面向对象】编程(Object Oriented Programming)。
那什么是【面向过程】,什么又是【面向对象】呢?
举个例子:如何把大象放进冰箱里面?这个问题的答案肯定很多人都知道:打开冰箱->放入大象->关上冰箱。这就是一个很典型的【面向过程】的解决问题的思维,解决问题是从步骤下手的。如果是【面向对象】的思维,那么我会先抽象一个冰箱出来,这个冰箱能够打开和关闭,这是冰箱这个事物的能力;然后再抽象一个大象出来,这个大象可能会变大和缩小,这是大象这个事物的能力;那么解决这个问题就是:冰箱.打开->大象.缩小->冰箱.关闭。
可以看出【面向过程】更注重的是解决问题的过程,而【面向对象】更注重的是参与这个问题的事物是什么,他们有什么属性,他们能够干什么。 我们把这样的事物称之为“对象”(object)。
C++在C的基础上增加了面向对象编程的语法,使用起来十分具有灵活性。这篇文章只是简单的入门,以后我会用比较多的博文来更详细说明C++类与对象编程的一些细节。
面向对象的特点
- 封装性
- 继承性
- 多态性
继承和多态是【面向对象】的最重要的两个特点,也他的最大优势。这些后期都会讲到,这里就大概感受一下什么是封装性吧。
类与对象
什么是类
这里引用百度百科的定义吧:
一个共享相同结构和行为的对象的集合。类(Class)定义了一件事物的抽象特点。通常来说,类定义了事物的属性和它可以做到的(它的行为)。举例来说,“狗”这个类会包含狗的一切基础特征,例如它的孕育、毛皮颜色和吠叫的能力。类可以为程序提供模版和结构。一个类的方法和属性被称为“成员”。
简单来说类就是一个模板,通过这个模板可以刻画出许多拥有相同基础特征的不同事物。
在C++中,我们将类的属性称作类的成员变量,将类的方法称作类的成员函数。
什么是类的实例化
说完类的定义,我们来说说类的实例化。说到底,“狗”是一个抽象的概念,而隔壁老王家的哈士奇波比是个具体的存在。我们说波比是“狗”这个概念的一个实例,或者说波比是“狗”这个类实例化的一个对象。
类的声明与定义
我们先来定义一个游戏角色类,看以下代码:
class Character {
int hp;
int mp;
};
学过C语言的同学不难看出来,这不就是struct嘛。对没错,C++的class就是用struct来实现的,而且和struct没什么区别。唯一的区别就是,class定义好之后,如果不特定标明属性或者方法是公有的(public)还是私有的(private),那就都默认是私有的,而struct默认是公有的。(还有一个protected属性是跟继承有关的,以后再说)
公有和私有
公有和私有也很好理解:公有就是我可以用,别人也可以用;私有就是除了我自己之外别人都不可以用。放在类与对象里面理解就是:类实例化了一个对象,这个对象的公有成员变量你可以在任何地方任何时刻修改,但是私有成员变量只能由对象自己来修改。
优化一下以上代码:
class Character {
private:
int hp;
int mp;
public:
void heal(int heal_hp){ hp += heal_hp; }
};
这里我把角色的hp和mp给私有化了,以保证外部不能随意修改他们的值。然后我定义了一个公有的函数heal,用于给角色恢复生命值,这样外部就可以通过调用这个成员函数来改变角色的hp。这就是封装,我不希望别人能够随意玩弄我角色,你只能用我规定的方法来玩弄他。
构造函数
我们在创建角色的时候,总得先给这个角色一些初始的数值吧,而且上面也说了只有【声明】没有【定义】是有可能会出问题的。构造函数就是干这事的,他在类被实例化的时候被调用:
class Character {
private:
int hp;
int mp;
public:
//这是编译器自动补充的构造函数,但是如果自己实现了,编译器就不会自动合成了
//Character(){};
Character() {
//函数名必须和类名相同,并且没有返回值
hp = 100;
mp = 50;
cout << "新建英雄成功!" << endl;
}
void heal(int heal_hp) { hp += heal_hp; }
};
对其进行实例化:
Character mario = Character(); //新建英雄成功!
Character link; //新建英雄成功!
但是我们还希望我们的角色的hp和mp由我们自己来决定,那么就可以用到我上一篇博客说的函数重载了:
public:
//这是编译器自动补充的构造函数,但是如果自己实现了的话,编译器就不会补充了
//Character(){};
Character() {
//函数名必须和类名相同,并且没有返回值
hp = 100;
mp = 50;
cout << "新建英雄成功!" << endl;
}
Character(int hp, int mp) {
//this指针,用于指向实例化的对象
this->hp = hp;
this->mp = mp;
cout << "新建英雄成功!!!" << endl;
}
Character mario = Character(999, 999); //新建英雄成功!!!
Character link(999, 999); //新建英雄成功!!!
总结
入门介绍先到这里,本篇的主要目的不是深入的探讨,而是在于建立面向对象编程的认知。更深更细的东西我会在下一篇博文专门挑出来说的,也会再次谈到构造函数的。
我刚开始学面向对象编程的时候,一直有一个困惑,那就是我既然是要修改或者访问类里面的属性值,我为啥不直接将他设为public,这样不是更方便。为何要大费周章地专门写一个set函数去修改,专门写一个get函数去获取。后来在我看了浙江大学翁恺老师的C++面向对象课程之后,翁恺老师的解释令我恍然大悟。
他说将成员变量封装起来相当于把肌肉包在皮肤里面,我们可以通过大脑去控制肌肉的收缩,但是我们不会说拿一个电线,把电线接到肌肉上面去,通过电流让肌肉收缩。这个是可以实现,但是没有人会去这么做,太危险了。类的封装也是一个道理。
希望这个解释,也能帮助有和我一样困惑的同学。