一、继承的基本概念
在C++中,继承是面向对象编程的一个重要特性。通过继承,我们可以将一个已有的类的属性和方法继承到新的类中,从而避免代码的冗余。在继承中,我们有许多不同的类型,其中最常用的是公有继承。
公有继承是指从一个基类派生出一个新的派生类,新的派生类可以访问基类公有成员和方法。基类的私有成员和方法虽然不能被派生类直接访问,但它们可以通过基类的公有成员和方法来间接访问。下面是一个简单的公有继承的例子:
class BaseClass { public: void print() { cout << "This is a base class." << endl; } }; class DerivedClass : public BaseClass { public: void display() { cout << "This is a derived class." << endl; } }; int main() { DerivedClass obj; obj.print(); // 调用基类的公有成员函数 obj.display(); // 调用派生类的成员函数 return 0; }
在上面的例子中,我们定义了一个基类BaseClass和一个派生类DerivedClass。DerivedClass中使用public关键字对BaseClass进行继承,并定义了一个自己的公有成员函数display来显示一个字符串。在主函数中,我们创建了DerivedClass的一个实例obj,并通过obj调用了基类的公有成员函数print和派生类的公有成员函数display。
二、继承方式的选择
在使用继承的时候,我们需要根据具体情况来选择继承的方式。在C++中,我们有三种继承方式:公有继承、保护继承和私有继承。其中,公有继承是最常用的一种方式。在选择继承方式时,我们需要考虑以下几个方面:
1、可访问性
不同的继承方式会导致不同的成员可访问性。公有继承使得基类的公有成员和方法可以在派生类中直接访问,而保护继承和私有继承则限制了这种访问。因此,如果我们需要在派生类中直接访问基类的公有成员和方法,那么我们应该选择公有继承。
2、派生类的性质
派生类的性质和继承方式也有关系。如果我们需要定义一个新的类来扩展基类的功能,我们就应该使用公有继承。如果我们想要在一个新的类中实现基类的某一部分功能,而不是扩展基类的功能,我们可以考虑使用保护继承或私有继承。
3、基类的可用性
在某些情况下,我们需要将基类的某些成员和方法保护起来,防止派生类的误操作。这时候,我们可以使用保护继承或私有继承。如果我们不需要保护基类的成员和方法,我们可以使用公有继承。
三、注意事项
1、构造函数和析构函数的继承
在使用公有继承时,派生类会自动继承基类的构造函数和析构函数。在派生类的构造函数中,我们需要调用基类的构造函数来完成基类对象的初始化。在派生类的析构函数中,我们需要手动释放基类对象的资源。下面是一个例子:
class BaseClass { public: BaseClass() { cout << "BaseClass constructor." << endl; } ~BaseClass() { cout << "BaseClass destructor." << endl; } }; class DerivedClass : public BaseClass { public: DerivedClass() { cout << "DerivedClass constructor." << endl; } ~DerivedClass() { cout << "DerivedClass destructor." << endl; } }; int main() { DerivedClass obj; return 0; }
在上面的例子中,我们定义了一个BaseClass和一个DerivedClass。在DerivedClass中使用public关键字对BaseClass进行公有继承。在派生类的构造函数中,我们需要明确调用基类的构造函数,否则编译器会自动调用基类的默认构造函数。在派生类的析构函数中,我们需要手动释放基类对象的资源,否则编译器会自动调用基类的析构函数。
2、虚函数的覆盖
在基类和派生类中可以定义虚函数。派生类可以重新定义基类中的虚函数,这样派生类的对象就会调用它自己的虚函数而不是基类的虚函数。重新定义基类虚函数的过程称为覆盖(override)。
class BaseClass { public: virtual void print() { cout << "This is a base class." << endl; } }; class DerivedClass : public BaseClass { public: void print() { cout << "This is a derived class." << endl; } }; int main() { DerivedClass obj; BaseClass* ptr = &obj; ptr->print(); // 调用派生类的成员函数 return 0; }
在上面的例子中,我们定义了一个BaseClass和一个DerivedClass。在BaseClass中定义了一个虚函数print,在DerivedClass中重新定义了print。在主函数中,我们创建了DerivedClass的一个实例obj,并通过基类指针ptr调用了基类的虚函数print。由于ptr指向的是派生类的对象,因此实际上调用的是派生类的print函数。
3、公有继承和类型转换
在C++中,我们可以将一个派生类的指针或引用转换为基类的指针或引用,这称为向上转型。由于公有继承使得基类中的所有公有成员和方法都可以在派生类中访问,因此向上转型是安全的。下面是一个例子:
class BaseClass { public: void print() { cout << "This is a base class." << endl; } }; class DerivedClass : public BaseClass { public: void display() { cout << "This is a derived class." << endl; } }; int main() { DerivedClass obj; BaseClass* ptr = &obj; // 向上转型 ptr->print(); // 调用基类的成员函数 return 0; }
在上面的例子中,我们定义了一个BaseClass和一个DerivedClass。在主函数中,我们创建了DerivedClass的一个实例obj,并使用BaseClass的指针ptr指向obj。由于公有继承,ptr可以直接访问基类中的公有成员函数print。
4、构造函数和析构函数的调用顺序
在使用公有继承时,基类和派生类的构造函数和析构函数会按照特定的顺序调用。在对象创建时,首先会调用基类的构造函数,然后才调用派生类的构造函数。在对象销毁时,先调用派生类的析构函数,然后才调用基类的析构函数。下面是一个例子:
class BaseClass { public: BaseClass() { cout << "BaseClass constructor." << endl; } ~BaseClass() { cout << "BaseClass destructor." << endl; } }; class DerivedClass : public BaseClass { public: DerivedClass() { cout << "DerivedClass constructor." << endl; } ~DerivedClass() { cout << "DerivedClass destructor." << endl; } }; int main() { BaseClass* ptr = new DerivedClass(); delete ptr; return 0; }
在上面的例子中,我们定义了一个BaseClass和一个DerivedClass。在主函数中,我们使用new运算符创建一个DerivedClass的对象,并将该对象的地址赋给BaseClass指针ptr。在程序结束时,我们使用delete运算符释放ptr指向的内存。由于公有继承,对象销毁的顺序是先调用派生类的析构函数,然后才调用基类的析构函数。