C++学习笔记4--静态属性 静态方法 虚方法 抽象方法 多态性 析构函数 运算符重载 多继承 虚继承

来源:互联网 发布:mac os 10.12官方下载 编辑:程序博客网 时间:2024/05/23 00:10

静态属性和静态方法

我们真正需要的是一个只在创建或删除对象时候才允许访问的计数器。

这个问题必须使用 C++ 的静态属性和静态函数才能完美地得到解决。

 C++ 允许我们把一个或多个成员声明为属于某个类,而不是仅属于该类的对象。(就是说这个成员仅能让该类强暴)

这么做的好处是程序员可以在没有创建任何对象的情况下调用有关的方法。

 另外一个好处是能够让有关的数据仍在该类的所有对象间共享。

创建一个静态属性和静态方法:

只需要在它的声明前加上 static 保留字即可

#include <iostream>
#include <string>

class Pet
{
public:
 Pet(std::string theName);
 ~Pet();

 static int getCount();//静态方法
protected:
 std::string name;

private:
 static int count;//静态属性
};
/*
static标识的,在类加载的时候创建,属于整个类,通过类名调用,不属于类实例。
并且static属性被类一个对象修改的话,其他对象用这个属性的时候就是修改过的值了。
因为他属于类,打个比方,屋子里有一桶水,甲去接一杯水,桶里就少了一杯,乙再去接水的时候自然就不满了。
静态方法内不能直接调用实例方法,因为静态方法初始化的时候类对象还没有创建,除非你在static方法里面new一个对象。
不能出现this关键字
其实类似于全局
*/

class Dog : public Pet
{
public:
 Dog(std::string theName);
};

class Cat : public Pet
{
public:
 Cat(std::string theName);
};

int Pet::count = 0;//在这修改了静态属性

Pet::Pet(std::string theName)
{
 name = theName;
 count++;

 std::cout << "一只宠物出生了,名字叫做: " << name << "\n";
}

Pet::~Pet()
{
 count--;
 std::cout << name << "挂掉了\n";
}

int Pet::getCount()
{
 return count;
}

Dog::Dog(std::string theName) : Pet(theName)
{
}

Cat::Cat(std::string theName) : Pet(theName)
{
}

int main()
{
 Dog dog("Tom");//把字符串"Tom" 传结dog的构造函数再调用Pet的构造函数
 Cat cat("Jerry");

 std::cout << "\n已经诞生了" << Pet::getCount() << "只宠物!\n\n";


  Dog dog_2("Tom_2");
  Cat cat_2("Jerry_2");

  std::cout << "\n现在呢,已经诞生了" << Pet::getCount() << "只宠物!\n\n";
//这一对大括号是把这段代码做为一个区域

 std::cout << "\n现在还剩下 " << Pet::getCount() << " 只宠物!\n\n";
 return 0;
}

 

结果

 

一只宠物出生了,名字叫做: Tom
一只宠物出生了,名字叫做: Jerry

已经诞生了2只宠物!

一只宠物出生了,名字叫做: Tom_2
一只宠物出生了,名字叫做: Jerry_2

现在呢,已经诞生了4只宠物!

Jerry_2挂掉了
Tom_2挂掉了

现在还剩下 2 只宠物!

Jerry挂掉了
Tom挂掉了
Press any key to continue

没有大括号是这样的

一只宠物出生了,名字叫做: Tom
一只宠物出生了,名字叫做: Jerry

已经诞生了2只宠物!

一只宠物出生了,名字叫做: Tom_2
一只宠物出生了,名字叫做: Jerry_2

现在呢,已经诞生了4只宠物!


现在还剩下 4 只宠物!

Jerry_2挂掉了
Tom_2挂掉了
Jerry挂掉了
Tom挂掉了
Press any key to continue

虚方法

事实上在C和C++中,我们完全可以在没有创建变量的情况下为有关数据分配内存。

也就是直接创建一个指针并让它指向新分配的内存块:

int *pointer = new int;

*pointer = 110;

std::cout << *pointer;

delete pointer;

 

最后一步非常必要和关键,这是因为程序不会自动释放内存,程序中的每一个 new 操作都必须有一个与之对应的 delete 操作!

#include <iostream>
#include <string>

class Pet
{
public:
 Pet(std::string theName);

 void eat();
 void sleep();
 void play();

protected:
 std::string name;
};

class Cat : public Pet
{
public:
 Cat(std::string theName);

 void climb();
 void play();
};

class Dog : public Pet
{
public:
 Dog(std::string theName);

 void bark();
 void play();
};

Pet::Pet(std::string theName)
{
 name = theName;
}

void Pet::eat()
{
 std::cout << name << "正在吃东西!\n";
}

void Pet::sleep()
{
 std::cout << name << "正在睡大觉\n";
}

void Pet::play()
{
 std::cout << name << "正在玩儿\n";
}

Cat::Cat(std::string theName) : Pet(theName)
{
}

void Cat::climb()
{
 std::cout << name << "正在爬树!\n";
}

void Cat::play()
{
 Pet::play();
 std::cout << name << "玩毛线球!\n";
}

Dog::Dog(std::string theName) : Pet(theName)
{
}

void Dog::bark()
{
 std::cout << name << "旺~旺~\n";
}

void Dog::play()
{
 Pet::play();
 std::cout << name << "正在追赶那只该死的猫!\n";
}


int main()
{
 Pet *cat = new Cat("加菲");// 这里用指针 new一个
 Pet *dog = new Dog("欧迪");

 cat->sleep();
 cat->eat();
 cat->play();

 dog->sleep();
 dog->eat();
 dog->play();

 delete cat;
 delete dog;
 return 0;
}

程序与我们的预期不符:我们在 Cat 和 Dog 类里对 play() 方法进行了覆盖,但实际上调用的是 Pet::play() 方法而不是那两个覆盖的版本。WHY??

使用虚方法

cat 和 dog 在编译时都是 Pet 类型指针,编译器就认为两个指针调用的 play() 方法是

Pet::play() 方法,因为这是执行起来最快的解决方案。

而引发问题的源头就是我们使用了 new 在程序运行的时候才为 dog 和 cat 分配 Dog 类型和 Cat 类型的指针。

 

这些是它们在运行时才分配的类型,和它们在编译时的类型是不一样的!

为了让编译器知道它应该根据这两个指针在运行时的类型而有选择地调用正确的方法( Dog::play() 和 Cat::play() ),我们必须把这些方法声明为虚方法。

 

声明一个虚方法的语法非常简单,只要在其原型前边加上 virtual 保留字即刻。

virtual void play();

 

另外,虚方法是继承的,一旦在基类里把某个方法声明为虚方法,在子类里就不可能再把它声明为一个非虚方法了。

版本2

#include <iostream>
#include <string>

class Pet
{
public:
 Pet(std::string theName);

 void eat();
 void sleep();
 virtual void play();

protected:
 std::string name;
};

class Cat : public Pet
{
public:
 Cat(std::string theName);

 void climb();
 void play();
};

class Dog : public Pet
{
public:
 Dog(std::string theName);

 void bark();
 void play();
};

Pet::Pet(std::string theName)
{
 name = theName;
}

void Pet::eat()
{
 std::cout << name << "正在吃东西!\n";
}

void Pet::sleep()
{
 std::cout << name << "正在睡大觉\n";
}

void Pet::play()
{
 std::cout << name << "正在玩儿\n";
}

Cat::Cat(std::string theName) : Pet(theName)
{
}

void Cat::climb()
{
 std::cout << name << "正在爬树!\n";
}

void Cat::play()
{
 Pet::play();
 std::cout << name << "玩毛线球!\n";
}

Dog::Dog(std::string theName) : Pet(theName)
{
}

void Dog::bark()
{
 std::cout << name << "旺~旺~\n";
}

void Dog::play()
{
 Pet::play();
 std::cout << name << "正在追赶那只该死的猫!\n";
}


int main()
{
 Pet *cat = new Cat("加菲");// 这里用指针 new一个
 Pet *dog = new Dog("欧迪");

 cat->sleep();
 cat->eat();
 cat->play();

 dog->sleep();
 dog->eat();
 dog->play();

 delete cat;
 delete dog;
 return 0;
}

只是加了个virtual别的没改

结果

加菲正在睡大觉
加菲正在吃东西!
加菲正在玩儿
加菲玩毛线球!
欧迪正在睡大觉
欧迪正在吃东西!
欧迪正在玩儿
欧迪正在追赶那只该死的猫!
Press any key to continue

TIPS

 

TIPS one

如果拿不准要不要把某个方法声明为虚方法,那麽就把它声明为虚方法好了。

 

TIPS two

在基类里把所有的方法都声明为虚方法会让最终生成的可执行代码的速度变得稍微慢一些,但好处是可以一劳永逸地确保程序的行为符合你的预期!

 

TIPS three

在实现一个多层次的类继承关系的时候,最顶级的基类应该只有虚方法。

 

TIPS four

有件事现在可以告诉大家了:析构器都是虚方法!从编译的角度看,它们只是普通的方法。

如果它们不是虚方法,编译器就会根据它们在编译时的类型而调用那个在基类里定义的版本(构造器),那样往往会导致内存呢泄露!

抽象方法

 抽象方法(abstract method,也可以成为纯虚函数)是面向对象编程技术的另一个核心概念,在设计一个多层次的类继承关系时常会用到。

把某个方法声明为一个抽象方法等于告诉编译器这个方法必不可少,但我现在(在这个基类里)还不能为它提供一个实现!

其实在之前我们已经见过一个应该被声明为抽象方法的好例子了,没错,就是 Pet::play() 方法。

为什么?

 

上一个例子中,现实中既不存在什么都玩的宠物,也不存在什么都能玩的游戏。

每种宠物都有它自己的玩法,而我们的应对措施是输出一条消息说宠物正在玩。

现在既然知道了抽象方法这个概念,我们就再也用不着编写那些不必要的代码了。

抽象方法的语法很简单:在声明一个虚方法的基础上,在原型的末尾加上”=0”。(告诉编译器不用浪费时间在这个类里寻找这个方法的实现!)

virtual void play() = 0;

后面这个函数就不用实现了。

多态性

 多态性是面向对象程序设计的重要特征之一。

简单的说,多态性是指用一个名字定义不同的函数,调用同一个名字的函数,却执行不同的操作,从而实现传说中的”一个接口,多种方法”!

多态是如何实现绑定的?

 

编译时的多态性:通过重载实现

运行时的多态性:通过虚函数实现

编译时的多态性特点是运行速度快,运行时的多态性特点是高度灵活和抽象。

 析构函数解析

#include <iostream>

class ClxBase
{
public:
 ClxBase()
 {
 };

 virtual ~ClxBase()
 {
 };

 virtual void doSomething()
 {
  std::cout << "Do something in class ClxBase!\n";
 }
};

class ClxDerived : public ClxBase
{
public:
 ClxDerived()
 {
 };

 ~ClxDerived()
 {
  std::cout << "Output from the destructor of class ClxDerived!\n";
 }

 void doSomething()
 {
  std::cout << "Do something in class ClxDerived!\n";
 };
};

int main()
{
 ClxBase *pTest = new ClxDerived;
 pTest->doSomething();

 delete pTest;
 
 return 0;
}

结果

Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Press any key to continue

如果这样把

 ~ClxBase()
 {
 };

前的virtual去掉

#include <iostream>

class ClxBase
{
public:
 ClxBase()
 {
 };

 ~ClxBase()
 {
 };

 virtual void doSomething()
 {
  std::cout << "Do something in class ClxBase!\n";
 }
};

class ClxDerived : public ClxBase
{
public:
 ClxDerived()
 {
 };

 ~ClxDerived()
 {
  std::cout << "Output from the destructor of class ClxDerived!\n";
 }

 void doSomething()
 {
  std::cout << "Do something in class ClxDerived!\n";
 };
};

int main()
{
 ClxBase *pTest = new ClxDerived;
 pTest->doSomething();

 delete pTest;
 
 return 0;
}

结果

Do something in class ClxDerived!
Press any key to continue

只有new时才会出现上面问题

运算符重载

例如我们可以重载运算符 + , 如下:

int operator+(int a, int b)

{

return (a – b);

}

 

举个栗子:实现复数加法

(3, 4i)+ (5, -10i)= (8, -6i)

当我们还不知道重载,我们会这样做

#include <iostream>

class Complex
{
public:
    Complex();
    Complex(double r, double i);
    Complex complex_add(Complex &d);
    void print();

private:
    double real;
    double imag;
};

Complex::Complex()
{
    real = 0;
    imag = 0;
}

Complex::Complex(double r, double i)
{
    real = r;
    imag = i;
}

Complex Complex::complex_add(Complex &d)
{
    Complex c;

    c.real = real + d.real;
    c.imag = imag + d.imag;

    return c;
}

void Complex::print()
{
    std::cout << "(" << real << ", " << imag << "i)\n";
}

int main()
{
    Complex c1(3, 4), c2(5, -10), c3;

    c3 = c1.complex_add(c2);

    std::cout << "c1 = ";
    c1.print();
    std::cout << "c2 = ";
    c2.print();
    std::cout << "c1 + c2 = ";
    c3.print();

    return 0;
}

当我们朦胧懂得了重载,我们会这样做

#include <iostream>

// 演示对运算符"+"进行重载达到目的!

class Complex
{
public:
    Complex();
    Complex(double r, double i);
    Complex operator+(Complex &d);
    void print();

private:
    double real;
    double imag;
};

Complex::Complex()
{
    real = 0;
    imag = 0;
}

Complex::Complex(double r, double i)
{
    real = r;
    imag = i;
}

Complex Complex::operator+(Complex &d)
{
    Complex c;

    c.real = real + d.real;
    c.imag = imag + d.imag;

    return c;
}

void Complex::print()
{
    std::cout << "(" << real << ", " << imag << "i)\n";
}

int main()
{
    Complex c1(3, 4), c2(5, -10), c3;

    c3 = c1 + c2;

    std::cout << "c1 = ";
    c1.print();
    std::cout << "c2 = ";
    c2.print();
    std::cout << "c1 + c2 = ";
    c3.print();

    return 0;
}

一些规则

除了一下五个阿哥不允许重载外,其他运算符允许重载:

.(成员访问运算符)

.*(成员指针访问运算符)

::(域运算符)

sizeof(尺寸运算符)

?:(条件运算符)

重载不能改变运算符运算对象(操作数)个数。

重载不能改变运算符的优先级别。

重载不能改变运算符的结合性。

重载运算符的函数不能有默认的参数。

重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应该有一个是类对象或类对象的引用。(也就是说,参数不能全部都是

C++的标准类型,这样约定是为了防止用户修改用于标准类型结构的运算符性质)

运算符重载函数作为类友元函数

 不知道刚刚有没有鱼油有这样的疑问:”+”运算符是双目运算符,为什么刚刚的例子中的重载函数只有一个参数呢?

解答:实际上,运算符重载函数有两个参数,但由于重载函数是 Complex 类中的成员函数,有一个参数是隐含着的,运算符函数是用 this 指针隐式地访问类对象的成员。

return Complex(real+c2.real, imag+c2.imag);

return Complex(this->real+c2.real, this->imag+c2.imag);

return Complex(c1.real+c2.real, c1.imag+c2.imag);

 即通过对象 c1 调用运算符重载函数,并以表达式中第二个参数(运算符右侧的类对象c2)作为函数实参。

运算符重载函数除了可以作为类的成员函数外,还可以是非成员函数:放在类外,做 Complex 类的友元函数存在

这里有篇转载可以看一下。作业要研究

重载<<操作符

下面是一个 operator <<()函数的原型:

std::ostream& operator<<( std::ostream& os, Rational f );

 

第一个输入参数 os 是将要向它写数据的那个流,它是以“引用传递”方式传递的。

第二个输入参数是打算写到那个流里的数据值,不同的 operator <<()重载函数就是因为这个输入参数才相互区别的。

 

返回类型是 ostream 流的引用。一般来说,在调用 operator <<()重载函数时传递给它的是哪一个流,它返回的就应该是那个流的一个引用。

例。把上面重载+-*/也重载了

#include <iostream>
#include <string>
#include <stdlib.h>

class Rational
{
public:
 Rational(int num, int denom); //num = 分子 , denom = 分母

 Rational operator+(Rational rhs); // rhs == right hand side
 Rational operator-(Rational rhs);
 Rational operator*(Rational rhs);
 Rational operator/(Rational rhs);

private:
 void normalize(); // 负责对分数的简化处理

 int numerator; // 分子
 int denominator; //分母

 friend std::ostream& operator<<(std::ostream& os, Rational f);//友元函数,因为他要访问numerator和denominator
};

Rational::Rational(int num, int denom)
{
 numerator = num;
 denominator = denom;

 normalize();
}

// normalize() 对分数进行简化操作包括:
// 1.只允许分子为负数,如果分母为分数则把负数挪到分子部分,如1/-2 == -1/2
// 2.利用欧几里德算法(辗转求余原理)将分数进行简化: 2/10 => 1/5

void Rational::normalize()
{
 //确保分母为正
 if( denominator < 0 )
 {
  numerator = -numerator;
  denominator = -denominator;
 }

 //欧几里德算法

 int a = abs(numerator);
 int b = abs(denominator);

 //求出最大公约数
 while( b > 0 )
 {
  int t = a % b;
  a = b;
  b = t;
 }

 //分子、分母分别除以最大公约数得到最简化分数
 numerator /= a;
 denominator /=a;
}

// a   c   a*d   c*b   a*d  +  c*b
// - + - = --- + --- = -----------
// b   d   b*d   b*d =     b*d

Rational Rational::operator+(Rational rhs)
{
 int a = numerator;
 int b = denominator;
 int c = rhs.numerator;
 int d = rhs.denominator;

 int e = a*b +c*d;
 int f = b*d;

 return Rational(e, f);
}

//  a    c    a    -c
//  - -  -  = -  + --
//  b    d    b     d

Rational Rational::operator-(Rational rhs)
{
 rhs.numerator = -rhs.numerator;

 return operator+(rhs);
}

//  a     c     a*c
//  -  *  -  =  ----
//  b     d     b*d

Rational Rational::operator*(Rational rhs)
{
 int a = numerator;
 int b = denominator;
 int c = rhs.numerator;
 int d = rhs.denominator;

 int e = a*c;
 int f = b*d;

 return Rational(e, f);
}

//  a    c    a    d
//  - /  -  = - *  -
//  b    d    b    c

Rational Rational::operator/(Rational rhs)
{
 int t = rhs.numerator;
 rhs.numerator = rhs.denominator;
 rhs.denominator = t;

 return operator*(rhs);
}

std::ostream& operator<<(std::ostream& os, Rational f);

int main()
{
 Rational f1(2, 16);
 Rational f2(7, 8);

 //测试有理数加法运算
 std::cout << f1 << " + " << f2 << " == " << (f1+f2) << "\n";//+是字符串所以不调用重载函数

 //测试有理数减法运算
 std::cout << f1 << " - " << f2 << " == " << (f1-f2) << "\n";

 //测有理数乘法运算
 std::cout << f1 << " * " << f2 << " == " << (f1*f2) << "\n";

 //测有理数除法运算
 std::cout << f1 << " / " << f2 << " == " << (f1/f2) << "\n";
 
 return 0;
}

std::ostream& operator<<(std::ostream& os, Rational f)
{
 os << f.numerator << "/" << f.denominator;
 return os;
}

多继承

什么时候需要用到多继承?

只要你遇到的问题无法只用一个”是一个”关系来描述的时候,就是多继承出场的时候。

举个栗子:在学校里有老师和学生,他们都是人(Person),我们可以用”老师是人”和”学生是人”语法来描述这种情况。

从面相对象编程角度上来看,我么应该创建一个名为 Person 的基类和两个名为 Teacher 和 Student 的子类,后两者是从前者继承来的。

问题来了:有一部分学生还教课挣钱(助教),该怎么办?

 酱紫就存在了既是老师又是学生的复杂关系,也就是同时存在着两个”是一个”关系。

我们需要写一个 TeschingStudent 类让它同时继承 Teacher 类和 Student 类,换句话说,就是需要使用多继承。

 

基本语法:

class TeachingStudent : public Student, public Teacher

{

}

#include <iostream>
#include <string>

class Person
{
public:
 Person(std::string theName);

 void introduce();

protected:
 std::string name;
};

class Teacher : public Person
{
public:
 Teacher(std::string theName, std::string theClass);

 void teach();
 void introduce();

protected:
 std::string classes;
};

class Student : public Person
{
public:
 Student(std::string theName, std::string theClass);

 void attendClass();
 void introduce();

protected:
 std::string classes;
};

class TeachingStudent : public Student, public Teacher
{
public:
 TeachingStudent(std::string theName, std::string classTeaching, std::string classAttending);

 void introduce();
};

Person::Person(std::string theName)
{
 name = theName;
}

void Person::introduce()
{
 std::cout << "大家好,我是" << name << "。\n\n";
}

Teacher::Teacher(std::string theName, std::string theClass) : Person(theName)
{
 classes = theClass;
}

void Teacher::teach()
{
 std::cout << name << "教" << classes << "。\n\n";
}

void Teacher::introduce()
{
 std::cout << "大家好,我是" << name << ",我教" << classes << "。\n\n";
}

Student::Student(std::string theName, std::string theClass) : Person(theName)
{
 classes = theClass;
}

void Student::attendClass()
{
 std::cout << name << "加入" << classes << "学习。\n\n";
}

void Student::introduce()
{
 std::cout << "大家好,我是" << name << ",我在" << classes << "学习。\n\n";
}

TeachingStudent::TeachingStudent(std::string theName,
         std::string classTeaching,
         std::string classAttending)
         : Teacher(theName, classTeaching), Student(theName, classAttending)
{
}

void TeachingStudent::introduce()
{
 std::cout << "在家好,我是" << Student::name << "。我教" << Teacher::classes << ", ";
 std::cout << "同时我在" << Student::classes << "学习。\n\n";
}

int main()
{
 Teacher teacher( "小甲鱼", "C++入门班" );
 Student student( "小蒙子","C++入门班" );
 TeachingStudent teachingStudent( "哈哈", "C++入门班", "C++进阶班" );

 teacher.introduce();
 teacher.teach();
 student.introduce();
 student.attendClass();
 teachingStudent.introduce();
 teachingStudent.teach();
 teachingStudent.attendClass();
 
 return 0;
}

一些需要注意的地方

 

在使用多继承的时候,一定要特别注意继承了基类的多少个副本。

在使用多继承的时候,最安全最简明的做法是从没有任何属性且只有抽象方法的类开始继承。

 

按照上边这么做可以让你远离后代子类可能拥有好几个基类属性的问题。

这样的类又叫做接口(interface)。

虚继承

#include <iostream>
#include <string>

class Person
{
public:
 Person(std::string theName);

 void introduce();

protected:
 std::string name;
};

class Teacher : public Person
{
public:
 Teacher(std::string theName, std::string theClass);

 void teach();
 void introduce();

protected:
 std::string classes;
};

class Student : public Person
{
public:
 Student(std::string theName, std::string theClass);

 void attendClass();
 void introduce();

protected:
 std::string classes;
};

class TeachingStudent : public Student, public Teacher
{
public:
 TeachingStudent(std::string theName1, std::string theName2,std::string classTeaching, std::string classAttending);

 void introduce();
};

Person::Person(std::string theName)
{
 name = theName;
}

void Person::introduce()
{
 std::cout << "大家好,我是" << name << "。\n\n";
}

Teacher::Teacher(std::string theName, std::string theClass) : Person(theName)
{
 classes = theClass;
}

void Teacher::teach()
{
 std::cout << name << "教" << classes << "。\n\n";
}

void Teacher::introduce()
{
 std::cout << "大家好,我是" << name << ",我教" << classes << "。\n\n";
}

Student::Student(std::string theName, std::string theClass) : Person(theName)
{
 classes = theClass;
}

void Student::attendClass()
{
 std::cout << name << "加入" << classes << "学习。\n\n";
}

void Student::introduce()
{
 std::cout << "大家好,我是" << name << ",我在" << classes << "学习。\n\n";
}

TeachingStudent::TeachingStudent(std::string theName1,
         std::string theName2,
         std::string classTeaching,
         std::string classAttending)
         : Teacher(theName1, classTeaching), Student(theName2, classAttending)
{
}

void TeachingStudent::introduce()
{
 std::cout << "在家好,我是" << Student::name << "。我教" << Teacher::classes << ", ";
 std::cout << "同时我在" << Student::classes << "学习。\n\n";
}

int main()
{
 Teacher teacher( "小甲鱼", "C++入门班" );
 Student student( "小蒙子","C++入门班" );
 TeachingStudent teachingStudent( "哈哈","呵呵", "C++入门班", "C++进阶班" );

 teacher.introduce();
 teacher.teach();
 student.introduce();
 student.attendClass();
 teachingStudent.introduce();
 teachingStudent.teach();
 teachingStudent.attendClass();
 
 return 0;
}

 

结果

大家好,我是小甲鱼,我教C++入门班。

小甲鱼教C++入门班。

大家好,我是小蒙子,我在C++入门班学习。

小蒙子加入C++入门班学习。

在家好,我是呵呵。我教C++入门班, 同时我在C++进阶班学习。

哈哈教C++入门班。

呵呵加入C++进阶班学习。

Press any key to continue

多继承出现两个名字

TeachingStudent 类继承自 Teacher 和 Student 两个类,因而继承了两组 Person 类的属性,这在某些时候完全有道理,例如 classes 属性。

但它也有可能引起麻烦,例如发生在 name 属性身上的情况。

 

C++ 发明者也想到了这部分的冲突,因此为此提供了一个功能可以解决这个问题:虚继承(virtual inheritance)

通过虚继承某个基类,就是在告诉编译器:从当前这个类再派生出来的子类只能拥有那个基类的一个实例。 

虚继承的语法:

class Teacher : virtual public Person

{

}

 

这样做我们的问题就解决了:让 Student 和 Teacher 类都虚继承自 Person 类,编译器将确保从 Student 和 Teacher 类再派生出来的子类只能拥

有一份 Person 类的属性!

修改一下

#include <iostream>
#include <string>

class Person
{
public:
 Person(std::string theName);

 void introduce();

protected:
 std::string name;
};

class Teacher : virtual public Person //这里虚继承
{
public:
 Teacher(std::string theName, std::string theClass);

 void teach();
 void introduce();

protected:
 std::string classes;
};

class Student : virtual public Person //这里虚继承
{
public:
 Student(std::string theName, std::string theClass);

 void attendClass();
 void introduce();

protected:
 std::string classes;
};

class TeachingStudent : public Student, public Teacher
{
public:
 TeachingStudent(std::string theName,std::string classTeaching, std::string classAttending);

 void introduce();
};

Person::Person(std::string theName)
{
 name = theName;
}

void Person::introduce()
{
 std::cout << "大家好,我是" << name << "。\n\n";
}

Teacher::Teacher(std::string theName, std::string theClass) : Person(theName)
{
 classes = theClass;
}

void Teacher::teach()
{
 std::cout << name << "教" << classes << "。\n\n";
}

void Teacher::introduce()
{
 std::cout << "大家好,我是" << name << ",我教" << classes << "。\n\n";
}

Student::Student(std::string theName, std::string theClass) : Person(theName)
{
 classes = theClass;
}

void Student::attendClass()
{
 std::cout << name << "加入" << classes << "学习。\n\n";
}

void Student::introduce()
{
 std::cout << "大家好,我是" << name << ",我在" << classes << "学习。\n\n";
}

TeachingStudent::TeachingStudent(std::string theName,
         std::string classTeaching,
         std::string classAttending)
         :
                                 Teacher(theName, classTeaching),
         Student(theName, classAttending),
         Person(theName)//这里因为Teacher和Student都是虚继承所以都没有Person的拷贝所以还要在Person里调用他的构造方法
{
}

void TeachingStudent::introduce()
{
 std::cout << "在家好,我是" << name << "。我教" << Teacher::classes << ", "; //这里因为继承自Person所以只能用name
 std::cout << "同时我在" << Student::classes << "学习。\n\n";
}

int main()
{
 Teacher teacher( "小甲鱼", "C++入门班" );
 Student student( "小蒙子","C++入门班" );
 TeachingStudent teachingStudent( "哈哈", "C++入门班", "C++进阶班" );

 teacher.introduce();
 teacher.teach();
 student.introduce();
 student.attendClass();
 teachingStudent.introduce();
 teachingStudent.teach();
 teachingStudent.attendClass();
 
 return 0;
}

0 0
原创粉丝点击