引入类之前,首先引入一个古老的话题:类别,比如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  这样的限定方式,这样即使两种方法使用了同一个名字,但由于处于不同的类作用域中,两者的同时存在并不冲突!

c++入门之浅入浅出类——分享给很多想形象理解的人的更多相关文章

  1. 浅入深出Vue:代码整洁之封装

    深入浅出vue系列文章已经更新过半了,在入门篇中我们实践了一个小小的项目. <代码整洁之道>一书中提到过一句话: 神在细节中 这句话来自20世纪中期注明现代建筑大师 路德维希·密斯·范·德 ...

  2. Spring浅入浅出——不吹牛逼不装逼

    Spring浅入浅出——不吹牛逼不装逼 前言: 今天决定要开始总结框架了,虽然以前总结过两篇,但是思维是变化的,而且也没有什么规定说总结过的东西就不能再总结了,是吧.这次总结我命名为浅入浅出,主要在于 ...

  3. Spring的数据库编程浅入浅出——不吹牛逼不装逼

    Spring的数据库编程浅入浅出——不吹牛逼不装逼 前言 上文书我写了Spring的核心部分控制反转和依赖注入,后来又衔接了注解,在这后面本来是应该写Spring AOP的,但我觉得对于初学者来说,这 ...

  4. 浅入浅出EmguCv(一)OpenCv与EmguCv

    最近接触计算机视觉方面的东西,于是准备下手学习opencv,从官网下载windows的安装版,配置环境,一系列步骤走完后,准备按照惯例弄个HelloWord.也就是按照网上的教程,打开了那个图像处理领 ...

  5. 浅入深出之Java集合框架(上)

    Java中的集合框架(上) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到<浅入深出之Java集合框架 ...

  6. 浅入深出之Java集合框架(中)

    Java中的集合框架(中) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到<浅入深出之Java集合框架 ...

  7. 浅入深出之Java集合框架(下)

    Java中的集合框架(下) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,哈哈这篇其实也还是基础,惊不惊喜意不意外 ̄▽ ̄ 写文真的好累,懒得写了.. ...

  8. 浅入深出Vue:前言

    浅入深出Vue系列文章 之前大部分是在做后端,后来出于某些原因开始接触Vue.深感前端变化之大,各种工具.框架令人眼花缭乱.不过正是这些变化,让前端开发更灵活. 博主在刚开始时,参考官网的各个步骤以及 ...

  9. 包学会之浅入浅出Vue.js:结业篇(转)

    蔡述雄,现腾讯用户体验设计部QQ空间高级UI工程师.智图图片优化系统首席工程师,曾参与<众妙之门>书籍的翻译工作.目前专注前端图片优化与新技术的探研. 在第一篇<包学会之浅入浅出Vu ...

随机推荐

  1. C#面向对象 类的封装

    class student { public int _code; public int Code//属性 { //获取值 get { ; } //设置值 set { _code = value + ...

  2. 老K漫谈区块链的共识(3)——分布式系统和区块链共识

    1. 啥是分布式系统 当我们评价一个新的事物或者介绍一个新的技术的时候,我们不能架空历史和环境,新的事物不可能脱离历史和环境凭空诞生.任何新的事物和新的技术总是或多或少的,与旧的事件以及过去的技术有所 ...

  3. c/c++ allocator 使用

    allocator 使用 作用:只开辟空间,不调用构造函数 操作一览表 allocator<T> a 定义一个名为a的allocator对象,它可以为类型为T的对象分配内存 a.alloc ...

  4. Android中使用ViewGroup.removeViews()时出现NullPointException解决方案

    在ViewGroup的内部写一个动画效果,在效果结束之后会调用onAnimationEnd(Animation arg0),在此方法中如果直接使用removeViews()时,可能会出现NullPoi ...

  5. 你好,我是梁桐铭,.NET程序员,啰嗦下过去几年来的感悟吧

    序 所有的文章都会有序言,我的当然也不例外. 因为职业和工作的关系,很少有时间陪伴家人,感谢妻子10年以来的容忍和支持,感谢女儿给我生活带来的乐趣. 希望孩子长大了之后能热爱编程(可以不用以它谋生). ...

  6. JSONCkecker(Java语言版本)

    // MIT License // // Copyright (c) 2016 Michel Kraemer // Copyright (c) 2005 JSON.org // // Permissi ...

  7. 【Java多线程通信】syncrhoized下wait()/notify()与ReentrantLock下condition的用法比较

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6556925.html  一:syncrhoized使用同一把锁的多个线程用通信实现执行顺序的调度 我们知道,使 ...

  8. rem自适应

    //REM自适应 _resize(); window.addEventListener('resize', _resize, false); function _resize() { var devi ...

  9. (转)Spring Boot(三):Spring Boot 中 Redis 的使用

    http://www.ityouknow.com/springboot/2016/03/06/spring-boot-redis.html Spring Boot 对常用的数据库支持外,对 Nosql ...

  10. mybatis 常用

    1.新增时获得主键 <insert ...> <selectKey resultType="java.lang.Integer" keyProperty=&quo ...