继承是面向对象编程中的一个重要概念,它允许一个类(称为子类/派生类)继承另一个类(称为父类/基类)的属性和方法。子类可以重用父类的代码,并且可以添加自己的新属性和方法,或者重写父类的方法以满足自己的特定需求。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
class 派生类 : 继承方式 基类
class Person
{
protected:
string _name;
int _age;
};
class Student : public Person
{
protected:
int _stuid;
};
继承方式的关键字和访问修饰权限符是一样的,有三种:public、protected、private。相信大家都很熟悉。那这三种继承方式会有什么不同呢?情况有很多,但大家只要记住一句话,取权限小的那个,按照权限排序是public > protected > private。比如为public继承方式,我们用pubilc和基类的成员变量、成员函数的访问修饰权限符作比较取小的那个作为派生类继承过来的该成员变量或成员函数的访问权限符号。
先说几个结论:
代码演示:
class Person
{
protected:
string _name = "张三";
int _age = 18;
};
class Student : public Person
{
protected:
int _stuid = 10086;
};
int main()
{
Student s;
Person p;
p = s;
Person* p1 = new Student;
Person& p2 = s;
return 0;
}
我们思考一个问题?如果父类和子类的成员对象名一样会发生什么?
class A
{
protected:
int num = 10;
};
class B : public A
{
public:
void fun()
{
cout << num << endl;
}
protected:
int num = 20;
};
int main()
{
B b;
b.fun();
}
从结果可以看出,我们打印出来的是B中的num,那么A中的num去哪了?是被重新赋值了吗?答案是被隐藏了,也叫重定向,它在A的作用域中,可以通过::来访问。
class A
{
protected:
int num = 10;
};
class B : public A
{
public:
void fun()
{
cout << "B的num:" << num << endl;
cout << "A的num:" << A::num << endl;
}
protected:
int num = 20;
};
int main()
{
B b;
b.fun();
}
函数也是同理,只需要函数名称相同,基类的函数就会被隐藏起来。
class A
{
public:
int fun(int a, int b)
{
cout << "我是A的fun()" << endl;
return a + b;
}
protected:
int num = 10;
};
class B : public A
{
public:
void fun()
{
cout << "我是B的fun()" << endl;
}
protected:
int num = 20;
};
int main()
{
B b;
b.fun();
}
- 在继承体系中基类和派生类都有独立的作用域。
- 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏, 也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
- 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
- 注意在实际中在继承体系里面最好不要定义同名的成员
我们之前将如果我们不写,编译器会默认生成6个成员函数。我们这里讨论前4个函数在继承中的行为。
- 派生类的构造函数必须调用基类的构造函数来初始化基类那部分的成员变量。如果基类没有默认构造,那我们必须在派生类的初始化列表显示调用构造函数。(必须先构造父类再构造子类)
- 派生类的拷贝构造函数必须调用基类的拷贝构造函数来完成基类的初始化。
- 派生类的赋值必须要调用子类的赋值
- 必须先析构派生类然后再析构基类,因为如果先析构基类,会造成派生类如果使用基类的成员会造成野指针问题。
- 注:虽然派生类的析构函数名和基类的析构函数名不同,但由于后面的多态重写部分,析构函数名会被编译器处理destrutor()。所以基类析构函数不加virtual的情况下,派生类析构函数和父类析构函数构成隐藏关系。
- 派生类的析构函数不用显示调用基类的析构函数,编译器会在派生类析构以后,再自动调用基类的析构函数,保证了第4点。
class Person
{
public:
Person(string name,int age)
:_name(name)
,_age(age)
{
cout << "Person(string name,int age)" << endl;
}
Person(const Person& l)
{
_name = l._name;
cout << "Person(const Person& l)" << endl;
}
Person& operator==(const Person& l)
{
cout << "Person& operator==(const Person& l)" << endl;
if (this != &l)
{
_name = l._name;
}
return *this;
}
~Person()
{
cout << "~Person()" << endl;
}
protected:
string _name;
int _age;
};
class Student : public Person
{
public:
Student(string name, int age, int stid)
:Person(name, age)
, _stid(stid)
{
cout << "Student(string name, int age, int stid)" << endl;
}
Student(const Student& s)
:Person(s)
{
cout << "Student(const Student& s)" << endl;
_stid = s._stid;
}
Student& operator==(const Student& s)
{
cout << "Student& operator==(const Student& s)" << endl;
Person::operator==(s);
if (this != &s)
{
_stid = s._stid;
}
}
~Student()
{
cout << "~Student()" << endl;
}
protected:
int _stid;
};
int main()
{
Student s("张三",20,10086);
Student s1(s);
Student s2 = s1;
return 0;
}
爸爸的朋友并不是你的朋友,所以友元关系不能继承。故友元函数不能访问子类的私有和保护成员。
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子 类,都只有一个static成员实例。