C++面向对象(三):继承、多继承

C++面向对象(三):继承、多继承

继承

继承可以让子类拥有父类的所有成员(变量 \ 函数),提高代码的复用性

// 父类
class Person {
protected:
	int m_age;
public:
	void run() {
		cout << "Person::run()" << endl;
	}
};
// 子类 
class Student : public Person {
	int m_score;
public:
	void study() {
		m_age = 666;
		cout << "Student::study(" << m_age << ")" << endl;
	}
};
// 子类
class Worker : Person{
	int m_salary;
public:
	void work() {
		cout << "Worker::work()" << endl;
	}
};
int main() {
	Student student;
	student.run();
	student.study();
	getchar();
	return 0;
}

关系描述

Student是子类(subclass,派生类)

Person是父类(superclass,超类)

C++中没有像Java、OC的基类(类的老祖宗,其他所有的类最终都会继承自它。)

  • Java:java.lang.Object
  • OC:NSObject

内存布局

父类的成员变量在前,子类的成员变量在后:

struct Person {
	int m_age;
};
struct Student : Person {
	int m_score;
};
struct GoodStudent : Student {
	int m_money;
};

int main() {
	GoodStudent lubenwei;
    // 1
	lubenwei.m_age = 6;
    // 2
	lubenwei.m_score = 66;
    // 3
	lubenwei.m_money = 666;
	getchar();
	return 0;
}


成员访问权限

成员访问权限、继承方式有3种

  • public:公共的,任何地方都可以访问(struct默认)
  • protected:子类内部、当前类内部可以访问
  • private:私有的,只有当前类内部可以访问(class默认)
class Person {
//公有 
public:
	int m_id;
// 保护
protected:
// 私有
    int m_age;
private:
    int m_height;
};

// 继承方式
class Student : public Person {
	int m_score;
};
class Student : protected Person {
	int m_score;
};
// class声明,默认使用private继承
class Student : private Person {
	int m_score;
};

子类内部访问父类成员的权限,是以下2项中权限最小的那个

  • 成员本身的访问权限
  • 上一级父类的继承方式
// Student中Person的权限为private
class Person {
public:
	int m_age;
};
class Student : private Person {
// 可以访问m_age
	int m_score;
};

// 编译无法通过
class Person {
// 已经声明私有,所以外部无法再访问 
private:
	int m_age;
};
class Student : public Person {
	int m_score;
    void study() {
		m_age = 666;
	}
};

开发中用的最多的继承方式是public,这样能保留父类原来的成员访问权限,因为是取最小值。

无法直接访问时,利用公开的接口来调用继承的变量:

// 默认private
class Person {
	int m_age;
public:
	void setAge(int age) {
		m_age = age;
	}
	int getAge() {
		return m_age;
	}
};
// private 继承
class Student : Person {
public:
	int m_score;
	// 父类中所有成员都被继承,这里提供公有借口来访问私有成员
	void work() {
		setAge(6);
		int age = getAge();
		cout << age << endl;;
	}
};

访问权限不影响对象的内存布局。

访问权限是编译器特性,通过指针可以越过这些权限。

父类的构造函数

  • 子类的构造函数默认会调用父类的无参构造函数

  • 如果子类的构造函数显式地调用了父类的有参构造函数,就不会再去默认调用父类的无参构造函数

  • 如果父类缺少无参构造函数,子类的构造函数必须显式调用父类的有参构造函数

// 父类
class Person {
	int m_age;
public:
	Person() {
		cout << "Person::person()" << endl;
	}
	Person(int age) {
		cout << "Person::person(int age)" << endl;
	}
};
// 子类
class Student : public Person {
	int m_score;
public:
	// 子类的构造函数默认会调用父类的无参构造函数(在其内参数执行之前)
	// 因为如果父类的成员是私有的,就只能在父类中进行初始化成员变量
	Student() {
		cout << "Student::student()" << endl;
	}
	// 如果子类的构造函数显式地调用了父类的有参构造函数,就不会再去默认调用父类的无参构造函数
	Student() :Person(10) {
		cout << "Student::student()" << endl;
	}
};

这样可以在外面初始化父类的私有成员变量

// 父类
class Person {
	int m_age;
public:
	Person() :Person(0) {}
	Person(int age) :m_age(age) {}
	}
};
// 子类
class Student : public Person {
	int m_score;
public:
	Student() :Student(0, 0) {}
	Student(int age, int score) :m_score(score), Person(age) {}
};
// 初始化父类的成员
Student lubenwei(6, 666);

多继承

C++允许一个类可以有多个父类(不建议使用,会增加程序设计复杂度)

struct Student {
	int m_score;
	Student(int score) :m_score(score) {}
	void study () {
		cout << "Student::study() - score = " << m_score << endl;
	};
};
struct Worker {
	int m_salary;
	Worker (int salary) :m_salary (salary) {}
	void work () {
		cout << "Worker::work() - salary = " << m_salary << endl;
	};

};
// 内存按照继承顺序排布
struct Undergraduate : Student, Worker {
	int m_grade;
	// 多继承下的构造函数的调用
	Undergraduate (int grade, int salary, int score) 
		:m_grade (grade), Student(score), Worker(salary) {}
	void play () {
		cout << "Undergraduate::play() - grade = " << m_grade << endl;
	};
};

如果子类继承的多个父类都有虚函数,那么子类对象就会产生对应的多张虚表:

struct Student {
	virtual void study () {
		cout << "Student::study()" << endl;
	};
};
struct Worker {
	virtual void work () {
		cout << "Worker::work() " << endl;
	};
};
struct Undergraduate : Student, Worker {
    // 一张虚表
    void study () {
		cout << "Undergraduate::study()" << endl;
	};
    // 另一张虚表
    void work () {
		cout << "Undergraduate::work() " << endl;
	};
	void play () {
		cout << "Undergraduate::play()" << endl;
	};
};

同名函数,可以指明调用:

struct Student {
    void eat() {
        cout << "Student::eat()" << endl;
    }
};
struct Worker {
    void eat() {
        cout << "Worker::eat()" << endl;
    }
};
struct Undergraduate : public Student, public Worker {
    void eat() {
        cout << "Undergraduate::eat()" << endl;
    }
};
// 指明调用的哪个函数
Undergraduate ug;
ug.Student::eat();
ug.Worker::eat();
ug.Undergraduate::eat();

同名成员变量,可以指明访问哪个:

struct Student {
    int m_age;
};
struct Worker {
	int m_age;
};
struct Undergraduate : public Student, public Worker {
	int m_age;
};
// 指明访问的变量
Undergraduate ug;
ug.m_age = 6;
ug.Worker::m_age = 66;
ug.Student::m_age = 666;

这其实跟名字不一样时的逻辑是一致的。

菱形继承



菱形继承带来的问题 :

  • 最底下子类从基类继承的成员变量冗余、重复

  • 最底下子类无法访问基类的成员,有二义性

    struct Person {
    	int m_age;
    };
    struct Student : Person {
    	int m_score;
    };
    struct Worker : Person {
    	int m_salary;
    };
    struct Undergraduate : Student, Worker {
    	int m_grade;
    };
    int main() {
    	Undergraduate ug;
    	// 20个字节
    	cout << sizeof(ug) << endl;
    	// 两个age
    	ug.Student::m_age= 6;
    	getchar();
    	return 0;
    }
    

使用虚继承解决这个问题,Person类被称为虚基类:

在这里插入图片描述

// 没有使用虚继承为 10 * 2 + 3 = 23 * 4  92个字节 
// 使用虚继承为 10 + 3 + 2 = 15 * 4  60个字节 
struct Person {
	int m_age = 6;
	int m_age1 = 6;
	int m_age2 = 6;
	int m_age3 = 6;
	int m_age4 = 6;
	int m_age5 = 6;
	int m_age6 = 6;
	int m_age7 = 6;
	int m_age8 = 6;
	int m_age9 = 6;
};
struct Student : virtual Person {
	int m_score = 66;
};
struct Worker : virtual Person {
	int m_salary = 666;
};
struct Undergraduate : Student, Worker {
	int m_grade = 6666;
};
int main() {
	Undergraduate ug;
	cout << sizeof(ug) << endl;
	// 共用一个age,解决两个age重复的问题
	cout << sizeof(Undergraduate) << endl;
	getchar();
	return 0;
}

内存结构为



展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 游动-白 设计师: 上身试试
应支付0元
点击重新获取
扫码支付

支付成功即可阅读