引入类之前,首先引入一个古老的话题:类别,比如int ,char ,double;这些基本的类型方便了我们描述数据(请注意,这句话很重要),类型的存在就是为了方便我们描述数据的。而c++中的类其实作用也是:方便我们描述数据。因此,我们可以这么认为:引入类的作用,就是为了让我们可以像使用int 这些类型一样,来描述我们想要表达的数据。尽管很多时候我们需要把我们想描述的数据抽象化。因此,我们有理由将类和int,char这些基本类型一视同仁(这个思想是很重要的!!!,但要在编程的时候认识到这点并不容易)。而实质上,引入类的初衷:就是让我们能够像使用基本类型使用我们定义的类,并能够像基本类型那样操作自如。
再看一个基本的话题:比如定义了一个变量 ,比如int x;很不幸的是,我们已经养成了习惯,习惯把x叫作“变量”,同样的把int叫作类型,这对我们认识和使用类造成了很大的不便。在类中,也会定义类变量,但我们把它叫对象。比如,我们定义猫科动物为一类:class Cat{};通常类型名大写,请注意,这里的Cat是我们定义的类,或者说是类型。即Cat和int,char等并无差异性。比如我们可以通过这个Cat类型,定义一些猫科动物。比如Cat cat(就是定义了猫),Cat tiger(定义了老虎)。我们发现:猫和老虎都是猫科动物(就像1和2都是整数一样)。这里的cat,tiger,我们称之为对象。其实想叫作变量也可以,但还是跟着叫对象吧。
接下来,通过一段代码初步认识类:
# ifndef STOCK00_H
#define STOCK00_H
# include "string"
class Stock
{
private:
std::string company;
long shares;
double share_val;
double total_val;
void set_tot()//思考这个函数定义的意义何在
{
total_val = shares*share_val;
}
public:
Stock::Stock();
void acquire(const std::string &co, long n, double pr);
void buy(long num, double price);
void sell(long num, double price);
void updata(double price);
void show();
};
#endif //ifbdef...endif成对出现
这段代码定义了一个名叫Stock的类。首先,我们在这个代码中看到的是:public和private,中文意思公共和私有(这种描述和其功能很吻合)。共有的意思是对外公开的,客户可以对其访问的。我们也把这个叫做接口。私有的意思是用户对其不可直接访问的。上面代码可以看到:我们在private中定义了很多基本类型变量。我们把这些基本类型变量叫做数据成员。而public中定义的是函数成员,我们也叫做方法。当然不一定非要把数据成员都放在private中,而把函数成员都放在public中,取决于你想让用户访问什么。关于这方面,请具体参考c++ prime,并通过自己的实战体会。
在此,仅仅总结这么个东西(很重要):我们定义了一个类,比如猫科动物。我们首先想到的肯定是:既然是猫科动物,首先肯定要有一些描述它的信息:比如体重啊,毛色啊,名字啊(假如有),甚至眼睛大小啊等等。而这些其实本质上,就是我们说的数据成员,即我们需要定义一些基本类型变量来描述我们的类具有哪些特征。但是现在,我们是面向对象编程:我们需要封装。假如现在有一个用户手上有一只具有这些特征的值的猫,他想根据这些数据知道这个猫几岁了???。这便引入了这样一个问题:我们类的存在不只是为了存储一只猫的各类类型的值。(那样的话我们直接用结构体就好了,为何要使用类呢???),我们是想根据这些值能 做些什么比较方便的事,而这些事最终被反馈到用户。那么成员函数的存在就是:利用这些基本类型变量,完成某项工作。但用户并不需要知道怎么完成,我只需要知道猫有几岁???知道猫有几岁这个工作毫无疑问是通过函数完成的,或者说我们用什么方法(函数)来知道猫有几岁(过度很自然)。比如有一个函数indicate_age()可以帮我们做这件事,则用户可以通过调用类——猫中的indicate_age()方法来实现这个愿望。这一点也表明了,这个indicate_age()方法要放在public,这样用户才有访问使用它的权限。(假如用户不想让人知道名字,可以放在private中)
(如果清闲,可以思考Python中定义类有何异同)
请注意,上面那段代码中public中只是声明了函数原型,但是并没定义这些函数,当然我们可以在public中直接定义,但没必要,显得很啰嗦。下面给出这些函数的定义:
# include "iostream"
# include "stock00.h" void Stock::acquire(const std::string &co, long n, double pr)
{
company = co;
if (n < )
{
std::cout << "Number of shares can't be negative;"
<< company << "shares set to 0.\n";
shares = ;
}
else
shares = n;
share_val = pr;
set_tot();
} void Stock::buy(long num, double price)
{
if (num < )
{
std::cout << "Number of shares can't be negative."
<< "Transaction is aborted.\n";
}
else
{
shares += num;
share_val = price;
set_tot();
}
} void Stock::sell(long num, double price)
{
using std::cout;
if (num < )
{
cout << "Number of shares can't be negative."
<< "Transaction is aborted.\n";
}
else if (num>shares)
{
cout << "You can't sell more than you have!"
<< "Transaction is aborted.\n";
}
else
{
shares -= num;
share_val = price;
set_tot();
}
} void Stock::updata(double price)
{
share_val = price;
set_tot();
} void Stock::show()
{
std::cout << "Company:" << company
<< "Shares:" << shares << "\n"
<< "Shares Price:$" << share_val
<< "Total worth:$" << total_val << std::endl;
} Stock::Stock()
{
company = "no name";
shares = ;
share_val = 0.0;
}
我们重新用一个文件来定义这些函数:具体的细节不再赘述,想描述的是每个函数的抬头,都采用了Stock::,这其实是表明这些函数的作用域是类作用域,比如我们外面也声明了acquire()函数,那么两者是可以区分的。这个类作用域十分重要,具体的可以参考c++ prime,
在这里,我们发现,给出的第一个代码中,有这样一个函数:Stock::Stock(),这个函数对类的数据成员进行了赋值(实际上就是初始化)。这个函数有两个特点:1 没有返回类型;2 名字和类的名字要一致。我们把这种函数叫构造函数。实际编写代码中,我们有时候没有写构造函数,但是貌似并不是报错,但这并不意味着构造函数是非必须的。实际上,构造函数是必须的。我们在创建类 对象的时候(就是定义类 变量的时候)将先自动调用构造函数(即由系统自动调用),然后才能生成我们的类 对象。也就是构造函数是用来被创建对象的,从而构造函数是不能通过对象去调用的。这一是十分重要的。关于构造函数耳朵显示,和隐式初始化可以参考c++ primer。
上面完成了对一个类的定义。下面给出调用它的代码:
# include"iostream"
# include"stock00.h"
int main()
{
Stock fluffy_the_car;
fluffy_the_car.acquire("NanoSmart", , 12.50);
fluffy_the_car.show();
fluffy_the_car.buy(, 18.125);
fluffy_the_car.show();
fluffy_the_car.sell(, 20.00);
fluffy_the_car.show();
fluffy_the_car.buy(, 40.125);
fluffy_the_car.show();
system("pause");
//int i;
//std::cout << "i is:" << i << std::endl;//c++中未初始化造成报错,不同于C语言.
return ;
}
上面的代码是多么的简洁,假如我们是客户,我们只需要知道类方法怎么使用即可,即怎么调用接口,就能知道我们的猫有几岁。站在用户的角度讲,这段程序很简单!!!而这,就是类产生的目的!!!
C++中类的诞生是一种很重要的思想,而所有关于C++类相关的,最重要的思想在于:能够让我们像使用其他类一样方便的使用我们定义的自己数据类型。认识到这一点的重要性在于:当我们思想中认识到我们自己创建的类和int,char并无区别的时候,就意味着,int,char ,double这些基本类型所有的操作,定义的类对象也应该有:比如,初始化,赋值,甚至比较大小。具有指针,具有数据(类数组),可以将类作为函数的返回值和参数。所以,我们思考类的角度,应该从数据和结构的角度去思考!
关于类:还想说明两点很重要的地方:
1 声明类只是创建了对象的形式,而非创建对象本身。因此,在创建对象之前,我们所定义的东西,是不被分配内存空间的,比如当我们声明int类型的时候,只有定义了 int i,i才有内存空间
2 关于类的作用域。在很多地方,都讨论过作用域。作用域到底意味着什么???作用域意味着变量的生存周期,意味着作用域与作用域之间的独立性。而类的作用域,一个最明显的特征是,当我们定义类方法的时候,采用的是:classname ::funcname 这样的限定方式,这样即使两种方法使用了同一个名字,但由于处于不同的类作用域中,两者的同时存在并不冲突!