首页 > 代码库 > C++ Primer笔记14_面向对象程序设计

C++ Primer笔记14_面向对象程序设计

OOP概述

面向对象程序设计(object-oriented programming)的核心思想是数据抽象继承动态绑定

1.继承

类的一种层次关系,通常在层次关系的根部有一个基类,其他类则直接或间接的继承基类而来。这些继承而来的类称为派生类

基类希望它的派生类自定义适合自身的版本的函数,基类就将函数声明为虚函数,加上virtual关键字。


2.动态绑定

通过动态绑定,可以使用同一段代码处理基类和子类对象。

在C++中,当我们使用基类的引用或指针调用一个虚函数时会发生动态绑定。有虚函数(virtual)才会发生动态绑定

在C++中,基类必须将它的两种成员函数区分开,一种是希望派生类进行覆盖的函数;一种是希望派生类直接继承而不覆盖的函数

当且仅当通过指针或引用对虚函数调用时会在运行时被解析


3.派生类构造函数

派生类中含有从基类继承而来的成员,派生类必须使用基类的构造函数来初始化它的基类部分。

派生类可以访问基类的公有(public)成员和受保护(protected)成员。


4.纯虚函数

在函数体声明最后写=0,即可将一个函数声明为纯虚函数。

含有纯虚函数的类是抽象基类。抽象基类只负责定义接口,后续的其他类可以覆盖该接口。

不能直接创建一个抽象基类的对象(含有纯虚函数的类不能直接实例化)

派生类如果没有定义继承而来的纯虚函数,则派生类也是抽象类,不能实例化。


5.类的作用域

每个类有自己的作用域,在这个作用域内我们定义类的成员。

当存在继承关系时,派生类作用域嵌套在基类作用域内,如果一个名字在派生类的作用域内无法解析,则编译器将继续在外层的基类中寻找该名字的定义。

派生类的成员将隐藏同名的基类成员


6.隐藏、覆盖。重载的区别:

(覆盖即派生类自己实现了基类中同名的函数(虚函数), 函数覆盖发生在父类与子类之间,其函数名、参数类型、返回值类型必须同父类中的相对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体不同)


只要基类在定义成员函数时已经声明了virtual关键字,在派生类实现的时候覆盖该函数时,virtual关键字可加可不加,不影响多态的实现。
容易与隐藏混淆:
隐藏是指派生类的函数屏蔽了与其同名的基类函数,规则如下: 
1) 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。 
2) 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。


比如,在下面的程序中:

#include <iostream.h> 
class Base 
{ 
public: 
virtual void f(float x){ cout << "Base::f(float) " << x << endl; } 
void g(float x){ cout << "Base::g(float) " << x << endl; } 
void h(float x){ cout << "Base::h(float) " << x << endl; } 
}; 

class Derived : public Base 
{ 
public: 
virtual void f(float x){ cout << "Derived::f(float) " << x << endl; } 
void g(int x){ cout << "Derived::g(int) " << x << endl; } 
void h(float x){ cout << "Derived::h(float) " << x << endl; } 
}; 
通过分析可得: 
1) 函数Derived::f(float)覆盖了Base::f(float)。 
2) 函数Derived::g(int)隐藏了Base::g(float),注意,不是重载。 
3) 函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。 


7.例子

test.h

#ifndef _TEST_H
#define _TEST_H

using namespace std;
#include <string>

class Animal
{
public:
	Animal();
	Animal(int a);
	virtual ~Animal();
	
	virtual void shout();	
	virtual void fight() = 0;
	void eat();
	void sleep();
protected:
	int age;
};


class Person : public Animal
{
public:
	Person();
	Person(int a, string n);
	~Person();

	virtual void shout();//Cover!
	//virtual void shout()const;//Hide!
	virtual void fight();//Cover
	void eat(string &n);//Hide  not override!
	void sleep();//Hide not Cover!
	void show();
private:
	//int age;//Hide!
	string name;
};

#endif

test.cpp
#include <iostream>
#include "test.h"

Animal::Animal():age(0)
{
	cout << "Animal 1" << endl;
}

Animal::Animal(int a):age(a)
{
	cout << "Animal 2" << endl;
}

Animal::~Animal()
{
	cout << "~ Animal " << endl;
}

void Animal::shout()
{
	cout << "Animal Shout!" << endl;
}

void Animal::eat()
{
	cout << "Animal eat!" << endl;
}

void Animal::sleep()
{
	cout << "Animal sleep!" << endl;
}

//----------------------------------------------------------------

Person::Person()
{
	cout << "Person 1" << endl;
}

Person::Person(int a, string n):Animal(a), name(n)//call Base class Counstruction Fun
{
	cout << "Person 2" << endl;
}

Person::~Person()
{
	cout << "~ Person " << endl;
}

void Person::shout()
{
	cout << "Person Shout!" << endl;
}

void Person::fight()
{
	cout << "Person fight!" << endl;
}

/*
void Person::shout()const
{
	cout << "const Person Shout!" << endl;
}
*/

void Person::show()
{
	cout << "I'm Person, Age: " << age << " Name: " << name << endl;
}

void Person::eat(string &n)
{
	cout << "Person: " << name <<  " eat!" << endl;
}

void Person::sleep()
{
	cout << "Person sleep!" << endl;
}


main.cpp

#include <iostream>
#include "test.h"

int main()
{
	Animal *p = new Person(20, "July");
	p->shout();//run time bind!
	p->fight();
	p->eat();
	p->sleep();
	delete p;//~ Animal 
		 //when add virtual before ~Animal() will output : ~ Person ~ Animal
	//Animal *p = new Animal(20, "July"); the class has pure virtual functions cannot init!

//if shout() is declared as : void shout(); p->shout() output :Animal shout  not run time bind!

/*
	Person p(20, "Mike");
	Animal &refP = p;
	refP.shout();
*/

//	p->show();  Error Animal has no number of show

	return 0;
}

运行结果:

Animal 2
Person 2
Person Shout!
Person fight!
Animal eat!
Animal sleep!
~ Person 
~ Animal

可以得出这样一个结论,被隐藏的函数是不能实现多态的。只有覆盖virtual函数才能。

另外,基类析构函数需要加virtual,以便于正确的调用基类与派生类的析构函数,如果不加,delete p只会输出:

~Animal,而不会调用派生类的析构函数。