Part  4

一  this指针

有一个特殊的指针,它叫做this。我们从来没有见过他,但是他却从来都存在。我们通过一个典型的例子来认识它:

class Human {

char  fishc;

Human(char  fishc);

}

Human::Human(char  fishc){

fishc = fishc;

}

我们看到,在”fishc = fishc”之前,所有的语法都没有任何问题:

Human()构造器有一个名为fishc的参数

虽然他与Human类里边的属性同名,但却是不相干的两样东西,所以并没有错。

可是,问题是怎样才能让构造器知道哪个是参数,哪个是属性呢?

这时候,就需要用到他了 – this指针。

this -> fishc = fishc

现在编译器就懂了,赋值操作符的左边将被解释为当前对象的fishc属性,右边将被解释为构造器的传入来的fishc参数。

注意:

使用this指针的基本原则是:如果代码不存在二义性隐患,就不必使用this指针!

this指针在一些更加高级的方法里也会用到,但我们暂时不想把事情弄得那么复杂。。。。。

二 类的继承

继承是面对对象编程技术的一个核心概念,它使传统的软件开发模式发生了革命性的变化。

继承机制使得程序员可以创建一个类的堆叠层次结构,每个子类均将继承在它的基类里定义的方法和属性。

那么到底有啥作用?

简单地说,通过继承机制,程序员可以对现有的代码进行进一步的扩展,并应用在新的程序中。

那么我们就试着编写一个Animal类作为Turtle类和Pig类的基类。

基类:

基类是可以派生出其他的类,也称为父类或超类。比如这里的Animal类是基类。

子类:

子类是从基类派生出来的类,比如这里的Turtle类和Pig类是子类。

那么Animal类就拥有了Turtle类和Pig类的共同特征:吃东西、睡觉、流口水。

这里我们把这些动作都抽象化为方法

eat(), sleep(), drool();

代表吃东西、睡觉、流口水的eat(), sleep(), drool() 是Animal类里的方法,因为每只动物都会做这些动作。

而swim() 方法在Turtle类里实现,climb() 方法在Pig类里实现。

动物都有嘴巴,而嘴巴是名词不是动作,所以要翻译成类的属性,而不能翻译成类的方法。

我们将mouth转变为Animal类的一个成员变量(属性)。

语法

class  SubClass : public  SuperClass { … }

class  Pig : public  Animal { … }

Eg

#include <iostream>

#include <string>

class BaseClass

{

public:

    BaseClass();

    ~BaseClass();

    void doSomething();

};

class SubClass : public BaseClass

{

public:

    SubClass();

    ~SubClass();

};

BaseClass::BaseClass()

{

    std::cout << "进入基类构造器。。。。。\n";

    std::cout << "我在基类构造器里边干了某些事。。。。\n\n";

}

BaseClass::~BaseClass()

{

    std::cout << "进入基类析构器.......\n";

    std::cout << "我在基类析构器里边也干了某些事。。。。\n\n";

}

void BaseClass::doSomething()

{

    std::cout << "我干了某些事。。。。\n\n";

}

SubClass::SubClass()

{

    std::cout << "进入子类构造器.....\n";

    std::cout << "我在子类构造器里边还干了某些事.....\n\n";

}

SubClass::~SubClass()

{

    std::cout << "进入子类析构器......\n";

}

int main()

{

    SubClass subclass;

    subclass.doSomething();

    std::cout << "完事,收工!\n";

    return 0;

}

关于构造器的设计要越简明越好!我们应该只用它来初始化各种有关的属性。

作为一个基本原则,在设计、定义和使用一个类的时候,应该让它的每个组成部分简单到不能再简单!

最后一点别忘了,析构器的基本用途是对前面所做的事情进行清理。尤其是在使用了动态内存的程序里,析构器将至关重要!

三 访问控制

在此前的例子里,我们无论是Animal, Pig 和 Turtle 类的所有成员都是用 public: 语句声明。

所谓访问控制,就是C++ 提供了一种用来保护类里的方法和属性的手段。

这里所说的保护意思是对谁可以调用某个方法和访问某个属性加上一个限制。如果某个对象试图调用一个它无权访问的函数,编译器将报错。

我们看下C++中的访问级别:

级别

允许谁来访问

public

任何代码

protected

这个类本身和它的子类

private

只有这个类本身

利用访问级别来保护类里的方法和属性很简单,只要在类里的某个地方写出一个访问级别并在其后加上一个冒号,从那个地方开始往后的所有方法和属性都将受到相应的保护,直到遇到下一个访问级别或者到达这个类的末尾为止!

class Animal

{

public:

    std::string name;

    Animal(std::string theName);

    void eat();

    void sleep();

    void drool();

};

请看 name 属性的访问级别是 public,这就意味着任何代码都可以改变它的值。事实我们今后就完全通过 pig.name =         “小甲鱼” 来任意改名字了。

我们发觉,如没有任何限制,猪的名字一下子就可以改掉了。这种隐患对这个简单的小程序来说可能没什么大不了的,但如果是发生在一个大型的程序里就足以引发一个逻辑漏洞。

注:BUG无法避免的原因正是因为我们无法模拟各种情况的的输入和修改带来的影响。

就像我们不能随便改变一个人的身份证的名字一样,Animal 类里的 name 属性应该受到保护。

尝试一下:test2

一定要记住使用这些访问级别!即时只有你一个人在开发某个项目,全部记住各个类的调用方法也是一件困难的事情。

给每个方法和属性加上 protected 或 private 访问级别,就由编译器替你记住那些禁令并在你违反的时候发出警报。

使用访问级别对身为程序员的你只有好处,没有坏处!再优秀的程序员也需要这种机械的保护!

使用 private 的好处是,今后可以只修改某个类的内部实现,而不必重新修改整个程序。这是因为其他代码根本就访问不到 private 保护的内容,所以不怕”牵一发而动全身”的惨剧发生!

同一个类定义里可以使用多个 public:, private: 和 protected: 语句,但最好把你的元素集中到一个地方,这样代码的可读性会好很多。

在编写你的类定义代码时,应该从 public: 开始写起,然后是 protected:, 最后是 private:

虽然编译器并不挑剔这些顺序,但这么做的好处是 —— 当你想知道某个特定的类提供了哪些方法和属性时,好的顺序可以为你节省大量的时间!

课后想一想:

class Pig : public Animal { … } 是什么意思?!

从基类继承来的方法和属性的保护

class Pig : public Animal { … }

C++ 不仅允许你对在类里定义的方法和属性实施访问控制,还允许你控制子类可以访问基类里的哪些方法和属性。

public

是在告诉编译器:继承的方法和属性的访问级别不发生任何改变 – 即 public 仍可以被所有代码访问,protected 只能由基类的子类访问,privat 则只能由基类本身访问。

protected

把基类的访问级别改为 protected , 如果原来是 public 的话。这将使得这个子类外部的代码无法通过子类去访问基类中的 public 。

private

是在告诉编译器从基类继承来的每一个成员都当成 private 来对待,这意味着只有这个子类可以使用它从基类继承来的元素。

二 类的方法覆盖以及重载

通过之前的学习,我们已经知道了如何通过创建新的子类来重用现有的代码(继承)。

虽然这个方案可以让我们轻松解决许多现实世界里的问题,但在某些场合,却又显得不够用。

例如当我们需要在基类里提供一个通用的函数,但在它的某个子类里需要修改这个方法的实现,在 C++ 里,覆盖(overriding)就可以做到。

回到我们之前的例子,我们都知道,但凡是个动物都知道吃!那么吃我们就可以说是动物的一个共同特征,但我们知道不同动物会有不同的吃法。。。。。。

C++ 可以让我们很容易实现这种既有共同特征又需要在不同的类里有不同实现的方法。

我们需要做的是在类里重新声明这个方法,然后再改写一下它的实现代码(就像它是一个增加的方法那样)就行啦。

原版本

#include <iostream>

#include <string>

class Animal

{

public:

    Animal(std::string theName);

    void eat();

    void eat(int eatCount);

    void sleep();

    void drool();

protected:

    std::string name;

};

class Pig : public Animal

{

public:

    Pig(std::string theName);

    void climb();

};

class Turtle : public Animal

{

public:

    Turtle(std::string theName);

    void swim();

};

Animal::Animal(std::string theName)

{

    name = theName;

}

void Animal::eat()

{

    std::cout << "I'm eatting!" << std::endl;

}

void Animal::eat(int eatCount)

{

    std::cout << "我吃了" << eatCount << "碗馄饨!\n\n";

}

void Animal::sleep()

{

    std::cout << "I'm sleeping!Don't disturb me!\n" << std::endl;

}

void Animal::drool()

{

    std::cout << "我是公的,看到母的我会流口水,我正在流口水。。。\n" << std::endl;

}

Pig::Pig(std::string theName) : Animal(theName)

{

}

void Pig::climb()

{

    std::cout << "我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。\n" << std::endl;

}

Turtle::Turtle(std::string theName) : Animal(theName)

{

}

void Turtle::swim()

{

    std::cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。\n" << std::endl;

}

int main()

{

    Pig pig("小猪猪");

    Turtle turtle("小甲鱼");

    // std::cout << "这只猪的名字是: " << pig.name << std::endl;                // 错误

    // std::cout << "每只乌龟都有个伟大的名字: " << turtle.name << std::endl;   // 错误

    pig.eat();

    turtle.eat();

    pig.eat(15);

    pig.climb();

    turtle.swim();

    return 0;

}

1.覆盖

#include <iostream>

#include <string>

class Animal

{

public:

    Animal(std::string theName);

    void eat();

    void sleep();

    void drool();

protected:

    std::string name;

};

class Pig : public Animal

{

public:

    Pig(std::string theName);

    void climb();

    void eat();                     // new!

};

class Turtle : public Animal

{

public:

    Turtle(std::string theName);

    void swim();

    void eat();                     // new!

};

Animal::Animal(std::string theName)

{

    name = theName;

}

void Animal::eat()

{

    std::cout << "I'm eatting!" << std::endl;

}

void Animal::sleep()

{

    std::cout << "I'm sleeping!Don't disturb me!\n" << std::endl;

}

void Animal::drool()

{

    std::cout << "我是公的,看到母的我会流口水,我正在流口水。。。\n" << std::endl;

}

Pig::Pig(std::string theName) : Animal(theName)

{

}

void Pig::climb()

{

    std::cout << "我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。\n" << std::endl;

}

void Pig::eat()

{

    Animal::eat();

    std::cout << name << "正在吃鱼!\n\n";         // new!

}

Turtle::Turtle(std::string theName) : Animal(theName)

{

}

void Turtle::swim()

{

    std::cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。\n" << std::endl;

}

void Turtle::eat()

{

    Animal::eat();

    std::cout << name << "正在吃东坡肉!\n\n";     // new!

}

int main()

{

    Pig pig("小猪猪");

    Turtle turtle("小甲鱼");

    // std::cout << "这只猪的名字是: " << pig.name << std::endl;                // 错误

    // std::cout << "每只乌龟都有个伟大的名字: " << turtle.name << std::endl;   // 错误

    pig.eat();

    turtle.eat();

    pig.climb();

    turtle.swim();

    return 0;

}

  1. 重载

#include <iostream>

#include <string>

class Animal

{

public:

    Animal(std::string theName);

    void eat();

    void eat(int eatCount);

    void sleep();

    void drool();

protected:

    std::string name;

};

class Pig : public Animal

{

public:

    Pig(std::string theName);

    void climb();

};

class Turtle : public Animal

{

public:

    Turtle(std::string theName);

    void swim();

};

Animal::Animal(std::string theName)

{

    name = theName;

}

void Animal::eat()

{

    std::cout << "I'm eatting!" << std::endl;

}

void Animal::eat(int eatCount)

{

    std::cout << "我吃了" << eatCount << "碗馄饨!\n\n";

}

void Animal::sleep()

{

    std::cout << "I'm sleeping!Don't disturb me!\n" << std::endl;

}

void Animal::drool()

{

    std::cout << "我是公的,看到母的我会流口水,我正在流口水。。。\n" << std::endl;

}

Pig::Pig(std::string theName) : Animal(theName)

{

}

void Pig::climb()

{

    std::cout << "我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。\n" << std::endl;

}

Turtle::Turtle(std::string theName) : Animal(theName)

{

}

void Turtle::swim()

{

    std::cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。\n" << std::endl;

}

int main()

{

    Pig pig("小猪猪");

    Turtle turtle("小甲鱼");

    // std::cout << "这只猪的名字是: " << pig.name << std::endl;                // 错误

    // std::cout << "每只乌龟都有个伟大的名字: " << turtle.name << std::endl;   // 错误

    pig.eat();

    turtle.eat();

    pig.eat(15);

    pig.climb();

    turtle.swim();

    return 0;

}

对方法(函数)进行重载一定要有的放矢,重载的方法(函数)越多,程序就越不容易看懂。

在对方法进行覆盖(注意区分覆盖和重载)时一定要看仔细,因为只要声明的输入参数和返回值与原来的不一致,你编写出来的就将是一个重载方法而不是覆盖方法。这种错误以小甲鱼的个人经验告诉你:往往很难调试!

对从基类继承来的方法进行重载,程序永远不会像你预期的那样工作!(try)

#include <iostream>

#include <string>

class Animal

{

public:

    Animal(std::string theName);

    void eat();

    void sleep();

    void drool();

protected:

    std::string name;

};

class Pig : public Animal

{

public:

    Pig(std::string theName);

    void climb();

    void eat(int eatCount);

};

class Turtle : public Animal

{

public:

    Turtle(std::string theName);

    void swim();

};

Animal::Animal(std::string theName)

{

    name = theName;

}

void Animal::eat()

{

    std::cout << "I'm eatting!" << std::endl;

}

void Animal::sleep()

{

    std::cout << "I'm sleeping!Don't disturb me!\n" << std::endl;

}

void Animal::drool()

{

    std::cout << "我是公的,看到母的我会流口水,我正在流口水。。。\n" << std::endl;

}

Pig::Pig(std::string theName) : Animal(theName)

{

}

void Pig::climb()

{

    std::cout << "我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。\n" << std::endl;

}

void Pig::eat(int eatCount)

{

    std::cout << "我吃了" << eatCount << "碗馄饨!\n\n";

}

Turtle::Turtle(std::string theName) : Animal(theName)

{

}

void Turtle::swim()

{

    std::cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。\n" << std::endl;

}

int main()

{

    Pig pig("小猪猪");

    Turtle turtle("小甲鱼");

    // std::cout << "这只猪的名字是: " << pig.name << std::endl;                // 错误

    // std::cout << "每只乌龟都有个伟大的名字: " << turtle.name << std::endl;   // 错误

    pig.eat();

    turtle.eat();

    pig.eat(15);

    pig.climb();

    turtle.swim();

    return 0;

}

也就是说继承之后不能够进行重载了,只能够在同一个类之中进行重载,崽子类之中那叫做覆盖而不能叫重载

c++学习笔记 第四部分相关推荐

  1. ASP.NET Core 2 学习笔记(四)依赖注入

    原文:ASP.NET Core 2 学习笔记(四)依赖注入 ASP.NET Core使用了大量的依赖注入(Dependency Injection, DI),把控制反转(Inversion Of Co ...

  2. Spring Cloud 学习笔记(四)-Spring Cloud Hystrix

    Spring Cloud 学习笔记(四)-Spring Cloud Hystrix 由于前一阵子项目的原因,今天才继续弄上,今天想学习一下Hystrix组件 这个组件还挺抽象的,最开始我一直没太明白, ...

  3. OpenCV学习笔记(四十六)——FAST特征点检测features2D OpenCV学习笔记(四十七)——VideoWriter生成视频流highgui OpenCV学习笔记(四十八)——PCA算

    OpenCV学习笔记(四十六)--FAST特征点检测features2D 特征点检测和匹配是计算机视觉中一个很有用的技术.在物体检测,视觉跟踪,三维常年关键等领域都有很广泛的应用.这一次先介绍特征点检 ...

  4. OpenCV学习笔记(四十一)——再看基础数据结构core OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年 OpenCV学习笔记(四十三)——存取像素值操作汇总co

    OpenCV学习笔记(四十一)--再看基础数据结构core 记得我在OpenCV学习笔记(四)--新版本的数据结构core里面讲过新版本的数据结构了,可是我再看这部分的时候,我发现我当时实在是看得太马 ...

  5. Windows x64内核学习笔记(四)—— 9-9-9-9-12分页

    Windows x64内核学习笔记(四)-- 9-9-9-9-12分页 前言 9-9-9-9-12分页 实验一:线性地址转物理地址 页表基址 定位基址 PTE to PXE 实验二:通过页表基址定位各 ...

  6. Intel VT学习笔记(四)—— VMCS(下)

    Intel VT学习笔记(四)-- VMCS(下) 要点回顾 VM-Exit Information Guest state fields 代码实现 参考资料 要点回顾 在上一篇中,我们了解了如何设置 ...

  7. 软件调试学习笔记(四)—— 异常的处理流程

    软件调试学习笔记(四)-- 异常的处理流程 要点回顾 异常的处理流程 实验1:理解调试器与异常的关系 未处理异常:最后一道防线 实验2:理解UnhandledExceptionFilter执行流程 实 ...

  8. 消息机制学习笔记(四)—— 内核回调机制

    消息机制学习笔记(四)-- 内核回调机制 要点回顾 内核调用 实验1:理解内核调用 第一步:编译并运行以下代码 第二步:修改窗口过程函数,重新运行 KeUserModeCallback 实验2:在OD ...

  9. Windows异常学习笔记(四)—— 编译器扩展SEH

    Windows异常学习笔记(四)-- 编译器扩展SEH 要点回顾 编译器支持的SEH 过滤表达式 实验一:理解_try_except 实验二:_try_except 嵌套 拓展SEH结构体 scope ...

  10. Windows事件等待学习笔记(四)—— 事件信号量互斥体

    Windows事件等待学习笔记(四)-- 事件&信号量&互斥体 要点回顾 事件 实验:验证SignalState 第一步:编译并运行以下代码 第二步:观察结果 第三步:修改代码并执行 ...

最新文章

  1. 【控制】《鲁棒控制-线性矩阵不等式处理方法》-俞立老师-第11章-大系统的分散控制
  2. SpringSecurity鉴权流程分析 附源码注释,xdm,一起来看看吧
  3. python 依据某几列累加求和_Python爬虫笔记:爬取单个页面
  4. Android studio怎么配置javadoc生成项目API
  5. gulp教程之gulp-imagemin压缩图片
  6. 根本没人买!又一品牌宣布不再做手机了...
  7. RxJava Map操作详解
  8. Appium环境搭建2021年最新详细教程
  9. Android shape自定义形状,设置渐变色
  10. 【python】Python学到什么程度可以面试工作
  11. 达梦disql中“pagesize”的设置
  12. C/C++犯二程度计算
  13. 面试题,你为什么想做产品经理?
  14. OSChina 周三乱弹 —— 哽住
  15. 使用Matlab SPM12与MRIcroGL进行头核磁ROI的标准化与Overlap图的制作
  16. 本章讲述一些foolish技巧
  17. 直流无刷电机霍尔传感器2种安装方式
  18. 又一篇!天昊生物微生物16S扩增子绝对定量测序技术再发好文
  19. 论文种类分类Task4
  20. 没有目标的人是可悲的-2006

热门文章

  1. Kubernetes架构基础知识
  2. BUU-0CTFpiapiapia(反序列化字符串逃逸)
  3. current,present,recent 都是现在的,都是形容词
  4. 推荐双11书单,我们一起共读 36 + 1 本书
  5. CStdioFile类的使用1
  6. 腾讯云人脸识别 报错 Entry name ‘assets/detector/wb_net_2_bin.rpnproto‘ collided
  7. 小老弟研发之路面筋大汇总——关于C/C++(三)
  8. 论文翻译--毫米波三维全息摄影的轻量级FMIST启发稀疏重建网络(1)
  9. 2018年sfdc工作总结_Lightning公用组件_模糊搜索单选组件
  10. C++从键盘输入的方法