Chapter 12: Class Inheritance
Deriving a Class (类的派生)
inheritance
- 可以从一个类里面继承成员
- Base class(父类)
- Derived class(子类)
- C++ 支持多个继承和多层继承
- 多个继承:一个子类可以有多个父类
- 多层继承:一个子类可以被孙子类继承
class base
{
public:
int a;
int b;
};
class Derived: public Base
{
public:
int c;
};
class Derived: public Base1,public Base2{....};
Constructor
- 举例说明一个派生类构造函数的执行过程
- 申请内存
- 派生的构造函数被调用
- 首先通过父类的构造函数创建一个父类的对象
- 成员初始化列表初始化成员
- 最后执行子类构造函数的主体
class Derived: public Base
{
public:
int c;
Derived(int c): Base(c - 2, c - 1), c(c)
{
this->a += 3; //it can be changed after initialization
cout << "Constructor Derived::Derived(" << c << ")" << endl;
}
....
};
Destructor
- 子类的析构函数先被调用
- 然后父类的析构函数再被调用
#include <iostream>
using namespace std;
class Base
{
public:
int a;
int b;
Base(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
cout << "Constructor Base::Base(" << a << ", " << b << ")" << endl;
}
~Base()
{
cout << "Destructor Base::~Base()" << endl;
}
int product()
{
return a * b;
}
friend std::ostream & operator<<(std::ostream & os, const Base & obj)
{
os << "Base: a = " << obj.a << ", b = " << obj.b;
return os;
}
};
class Derived: public Base
{
public:
int c;
Derived(int c): Base(c - 2, c - 1), c(c)
{
this->a += 3; //it can be changed after initialization
cout << "Constructor Derived::Derived(" << c << ")" << endl;
}
~Derived()
{
cout << "Destructor Derived::~Derived()" << endl;
}
int product()
{
return Base::product() * c;
}
friend std::ostream & operator<<(std::ostream & os, const Derived & obj)
{
// call the friend function in Base class
os << static_cast <const Base&> (obj) << endl;
os << "Derived: c = " << obj.c;
return os;
}
};
int main()
{
{
Base base(1, 2);
cout << base << endl;
}
cout << "----------------------" << endl;
{
Derived derived(5);
cout << derived << endl;
cout << "Product = " << derived.product() << endl;
}
return 0;
}
out:
Constructor Base::Base(1, 2)
Base: a = 1, b = 2
Destructor Base::~Base()
----------------------
Constructor Base::Base(3, 4) #父类的构造函数先被调用
Constructor Derived::Derived(5)
Base: a = 6, b = 4
Derived: c = 5
Product = 120
Destructor Derived::~Derived() #子类的析构函数先被调用
Destructor Base::~Base()
Access Control (protected)
Member Access
-
Public members: accessible anywhere
-
Private member : only accessible to the members and friends of that class
只能在类的内部和友元函数中被访问
class Person{
private:
int n;// private member
public:
Person() : n(10){} //this ->n is accessible
Person(const Person& other): n(other.n){} // other.n is accessible
void set(int n) {this->n = n;} //this ->n is accessible
void set(const Person& other){this->n =other.n;}//this->n and other.n are accessible
};
class Base
{
protected:
int n;
private:
void foo1(Base& b)
{
n++; // Okay
b.n++; // Okay
}
};
class Derived : public Base
{
void foo2(Base& b, Derived& d)
{
n++; //Okay
this->n++; //Okay
//b.n++; //Error. You cannot access a protected member through base
d.n++; //Okay
}
};
void compare(Base& b, Derived& d) // a non-member non-friend function
{
// b.n++; // Error
// d.n++; // Error
}
Public Inheritance
-
Public members of base class
- 在子类中仍然时public
- 任何地方可以访问
-
Protected members of the base class
- 在子类中仍然是protected
- 只能在子类中访问
-
Private members of base class
- 无法在子类中访问
Protected inheritance
-
Public members and Protected members of base class
- 在子类中都变成protected members
- 只能在子类中访问到
-
Private members of base class
- 无法在子类中访问
Private inheritance
私有继承后子类就无法再被继承
-
Public members and Protected members of base class
- 在子类中都变成Private members
- 只能在子类中访问到
-
Private members of base class
- 无法在子类中访问
Static and Dynamic Binding
- 静态绑定:编译器哪一个函数被调用
- 动态绑定:在运行时确实被调用的函数
Virtual Functions(虚函数)
一个用和不用虚函数之间区别的例子
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string name;
Person(string n): name(n){}
void print()
//virtual void print()
{
cout << "Name: " << name << endl;
}
};
class Student: public Person
{
public:
string id;
Student(string n, string i): Person(n), id(i){}
void print()
{
cout << "Name: " << name;
cout << ". ID: " << id << endl;
}
};
void printObjectInfo(Person & p)
{
p.print();
}
int main()
{
{
Student stu("yu", "2019");
printObjectInfo(stu);
}
{
Person * p = new Student("xue", "2020");
p->print(); //if print() is not a virtual function, different output
delete p; //if its destructor is not virtual
}
return 0;
}
out:
// if print() is not a virtual function,print() of base class is called.
Name: yu
Name: xue
// if print() is a virtual function,print() of child class is called.
Name: yu. ID: 2019
Name: xue. ID: 2020
- 关键词 “ virtual” 会使得这个函数成为父类以及所有子类的虚函数
- 函数一旦变成虚函数,这个函数就会采用动态绑定,再运行时才被确定被调用函数
- 父类的函数被设置为虚函数,子类中同名同参数的函数自动变成虚函数
- 纯虚函数
- 无实现、无定义,只是一个接口
- 存在纯虚函数的这个类无法创建对象
- 一般这样的类,是当父类用
class Person2
{
public:
string name;
Person2(string n): name(n){}
virtual void print() = 0; // 一个纯虚函数
};
-
析构函数都是虚函数
如果析构函数不是虚函数,那么new一个子类对象赋给父类指针,程序结束后只会调用到父类的析构函数,而不会调用子类的析构函数,存在很多风险。
{
Person * p = new Student("xue", "2020");
p->print(); //if print() is not a virtual function, different output
delete p; //if its destructor is not virtual
}
Dynamic Memory Management
- 如果父类中需要动态内存管理,子类不需要
- 只在父类中进行Hard copy即可(重新自定义一个copy constructor和copy assignment operator)
- 如果子类也需要动态内存管理
- 在子类中再进行一次hard copy