✨博客主页 | ||
---|---|---|
何曾参静谧的博客 | ||
文章专栏 | ||
「C/C++」C/C++程序设计 | ||
全部专栏 | ||
「VS」Visual Studio | 「C/C++」C/C++程序设计 | 「UG/NX」BlockUI集合 |
「Win」Windows程序设计 | 「DSA」数据结构与算法 | 「UG/NX」NX二次开发 |
「QT」QT5程序设计 | 「File」数据文件格式 | 「PK」Parasolid函数说明 |
在C++面向对象编程中,继承是一种强大的机制,它允许我们创建一个新的类(派生类)来继承另一个类(基类)的属性和方法。这种机制促进了代码的重用和扩展性。在继承关系中,基类的成员函数在派生类中仍然有效,并且可以根据需要进行重写或扩展。本文将详细探讨C++继承中的函数,包括函数的继承、重写(覆盖)、隐藏、多态性以及函数访问权限的变化。
当派生类从基类继承时,基类的公有成员函数和保护成员函数会自动成为派生类的成员。这些成员在派生类中具有与在基类中相同的访问权限(除非在派生类中被进一步限制)。
class Base {
public:
void publicFunc() { /* ... */ }
protected:
void protectedFunc() { /* ... */ }
};
class Derived : public Base {
// Base::publicFunc() 和 Base::protectedFunc() 都是 Derived 的成员
};
在上面的例子中,Derived
类继承了 Base
类的 publicFunc()
和 protectedFunc()
成员函数。publicFunc()
在 Derived
类中仍然是公有的,而 protectedFunc()
在 Derived
类中保持保护状态。
重写(也称为覆盖)是指在派生类中重新定义基类中已经存在的虚函数。要重写一个函数,派生类中的函数必须具有与基类中的虚函数相同的名称、返回类型和参数列表。
class Base {
public:
virtual void virtualFunc() { /* 基类实现 */ }
};
class Derived : public Base {
public:
// 重写基类的虚函数
void virtualFunc() override { /* 派生类实现 */ }
};
在上面的例子中,Derived
类重写了 Base
类的 virtualFunc()
成员函数。override
关键字是C++11引入的,它用于明确指出一个函数是重写基类中的虚函数,如果重写不正确,编译器会报错。
与重写不同,如果派生类定义了一个与基类中的非虚函数同名的函数(即使参数列表不同),那么基类的该函数在派生类作用域内将被隐藏。这意味着通过派生类对象或指针无法访问基类的同名函数。
class Base {
public:
void func() { /* 基类实现 */ }
};
class Derived : public Base {
public:
// 隐藏基类的 func() 函数
void func(int) { /* 派生类实现 */ }
};
int main() {
Derived d;
d.func(1); // 调用 Derived::func(int)
// d.func(); // 错误:无法访问隐藏的 Base::func()
Base* b = &d;
b->func(); // 调用 Base::func(),因为 b 是基类指针
return 0;
}
在上面的例子中,Derived
类定义了一个与 Base
类中的 func()
同名的函数,但参数列表不同。这导致 Base
类的 func()
函数在 Derived
类作用域内被隐藏。
多态性是面向对象编程的一个重要特性,它允许我们通过基类指针或引用来调用派生类中的重写函数。要实现多态性,基类中的函数必须是虚函数。
class Base {
public:
virtual void show() { std::cout << "Base show" << std::endl; }
virtual ~Base() = default; // 虚析构函数,确保正确调用派生类的析构函数
};
class Derived : public Base {
public:
void show() override { std::cout << "Derived show" << std::endl; }
};
int main() {
Base* b1 = new Base();
Base* b2 = new Derived(); // 基类指针指向派生类对象
b1->show(); // 调用 Base::show()
b2->show(); // 调用 Derived::show(),体现多态性
delete b1;
delete b2;
return 0;
}
在上面的例子中,b2
是一个基类指针,但它指向一个 Derived
对象。当我们调用 b2->show()
时,实际上调用的是 Derived
类中的 show()
函数,这就是多态性的体现。
在继承中,基类的成员函数在派生类中的访问权限可能会发生变化。如果基类中的函数是公有的,那么在派生类中它仍然是公有的(除非在派生类中被进一步限制)。如果基类中的函数是保护的或私有的,那么在派生类中它仍然保持相应的访问权限。
然而,需要注意的是,即使基类中的函数是保护的或私有的,派生类仍然可以通过基类指针或引用来调用这些函数(如果它们是虚函数并被重写)。这是因为虚函数调用是在运行时解析的,而不是在编译时。
class Base {
protected:
void protectedFunc() { /* ... */ }
private:
void privateFunc() { /* ... */ }
public:
virtual void virtualFunc() { /* 基类实现 */ }
};
class Derived : public Base {
public:
// 无法直接访问 Base::privateFunc(),因为它在基类中是私有的
// 但可以重写 Base::virtualFunc()
void virtualFunc() override { /* 派生类实现 */ }
// 可以访问 Base::protectedFunc(),因为它在基类中是保护的
void callProtectedFunc() {
protectedFunc(); // 合法
// privateFunc(); // 错误:无法访问基类的私有成员函数
}
};
#include
#include
// 基类
class Base {
private:
char* data;
public:
// 基类的构造函数,用于分配内存
Base(const char* str = "") {
data = new char[strlen(str) + 1];
strcpy(data, str);
std::cout << "Base constructor called, data: " << data << std::endl;
}
// 基类的虚析构函数,确保正确调用派生类的析构函数
virtual ~Base() {
std::cout << "Base destructor called, deleting data: " << data << std::endl;
delete[] data;
}
// 基类的虚函数,用于演示多态性
virtual void show() {
std::cout << "Base show, data: " << data << std::endl;
}
};
// 派生类
class Derived : public Base {
private:
int value;
public:
// 派生类的构造函数,调用基类的构造函数并初始化自己的成员
Derived(int val, const char* str = "") : Base(str), value(val) {
std::cout << "Derived constructor called, value: " << value << std::endl;
}
// 派生类的析构函数,确保在基类析构函数之后调用
~Derived() {
std::cout << "Derived destructor called, value: " << value << std::endl;
}
// 重写基类的虚函数
void show() override {
std::cout << "Derived show, value: " << value << ", data: " << Base::data << std::endl;
}
};
int main() {
// 创建派生类对象,并调用构造函数和析构函数
Derived d(42, "Hello from Derived");
d.show(); // 调用派生类的 show() 函数
// 通过基类指针创建派生类对象,并调用构造函数和析构函数
Base* b = new Derived(100, "Hello from Base pointer to Derived");
b->show(); // 调用派生类的 show() 函数,体现多态性
// 释放内存
delete b; // 先调用派生类的析构函数,然后调用基类的析构函数
// 注意:main 函数结束时,d 对象会自动调用其析构函数
return 0;
}
C++ 继承中的函数涉及多个方面,包括函数的继承、重写(覆盖)、隐藏、多态性以及函数访问权限的变化。理解这些概念对于深入掌握C++的面向对象编程至关重要。通过合理地使用继承机制,我们可以创建更加灵活和可扩展的代码结构。