1.前言
结合理论知识与自己的理解写的博客,主要是为了记录自己学习过程中的一些理解。如有错误,还请指正。
2.子类继承
当一个类继承了另一个类后,无论是什么继承类型,它继承的都是一样的东西。即继承所有父类成员变量与除父类构造函数与析构函数外的成员函数。但并不是父类中的东西只要子类继承了就能访问,子类是否能访问主要取决于父类成员在父类中的访问权限与子类继承父类的继承方式。
3.子类构造函数
在C++中,调用子类构造函数构造子类对象之前需先调用父类构造函数构造父类对象。
Son(string game,string name,int age) :Father("爸",25)
从正常逻辑来理解,面向对象思想中的类与对象是现实世界实体与概念的抽象表示。类具有的方法是现实事物行为的抽象表示,类的成员变量是现实事物属性的抽象表示。因此理解类的相关概念也能联系现实生活来理解。在这里也是一样的,把父类与子类的关系联系到现实中。肯定是先构造父类才能再构造子类,就像先有爸爸,然后这个爸爸才能生出一个儿子一样。
从代码角度看,子类对象会继承父类的成员方法,如果被继承的某个父类方法能被子类调用。那么需要先构造父类对象,用来初始化从父类继承的数据 。因为被调用的父类函数可能使用了父类的成员变量,所以需要先初始化再使用。要不然就可能是未知的值,造成程序未知的错误。
看一个例子:
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
#include <Windows.h>
using namespace std;
class Father {
public:
Father() {
}
Father(string name, int age) {
this->name = name;
this->age = age;
cout << "调用父类构造函数" << endl;
}
int print() {
cout << "调用父类函数:" << endl;
cout << "父类的名字:" << name << endl;
cout << "父类的年龄:" << age << endl;
return 0;
}
~Father() {
cout << "调用父类析构函数" << endl;
}
private:
string name;
int age;
};
class Son :public Father{
public:
Son(string game,string name,int age) :Father() {
this->age = age;
this->name = name;
this->game = game;
cout << "调用子类构造函数" << endl;
}
int print1() {
cout << "调用子类函数:" << endl;
cout << "子类的名字:" << name << endl;
cout << "子类的年龄:" << age << endl;
cout << "子类爱玩的游戏:" << game << endl;
return 0;
}
~Son() {
cout << "调用子类析构函数" << endl;
}
private:
int age;
string name;
string game;
};
int main(void) {
Son son("方舟", "儿", 15);
son.print();
cout << sizeof(son) << endl;
system("pause");
return 0;
}
我重载了父类的构造函数,一个是无参构造函数,一个是有参构造函数。并在子类构造函数中显示地调用父类的无参构造函数。因此父类对象的成员变量没有被初始化。所以在子类调用父类的print()方法时输出的都是未初始化的值。
另外,补充一点关于函数调用的规则。调用方法时,会先在子类定义的方法里找,如果找不到,就在父类定义的方法里找,如果还找不到,就发生错误。所以如果子类重写了父类的同名函数,那么子类对象调用该函数时,会调用子类的函数。
4.C++子类对象的内存分布
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
#include <Windows.h>
using namespace std;
class DEMO {
public:
DEMO() {
}
~DEMO() {
}
private:
int data;
int age;
int length;
};
int main(void) {
DEMO demo;
cout << sizeof(demo) << endl;
system("pause");
return 0;
}
在没有虚函数的情况下(以下都是在不考虑虚函数与内存对齐的情况下进行的讨论),子类对象的内存中只有其从父类继承的成员变量与其自己定义的成员变量(静态成员变量也不在对象内存中)。而没有成员函数的空间。另外,从父类继承的成员变量是存放在子类成员变量之前的。
一开始我也不太理解,成员函数也是类的成员,为什么对象中没有它的空间呢?
类是事物的抽象表示,成员方法是事物行为的抽象,成员变量是事物属性的抽象。对于一个类实例化的对象而言,它们具有的行为与属性是一样的。唯一不同的就是属性的值。
举个例子: 现在有一个人类类,他有吃饭,睡觉等方法,也有睡眠时间,饭量,身高,体重等属性。那么它实例化的对象也有吃饭,睡觉方法,睡眠时间,饭量,身高,体重属性。区分类实例化后对象的就是这些属性的值。如:不同的人每天睡眠时间是不一样的,身高,体重也是不一样的。
回到程序中,方法与变量在类的定义中已经有了并且所有对象都是一样的,不同的只是对象变量的值。类的成员方法都是存放在代码区的。当一个对象需要调用函数时,只需要去代码区找到,代入每个对象具体的变量值就行了(有点像函数调用)。而并不需要在每个对象中存放成员方法。
|