C++中的继承是一种面向对象编程(OOP)的特性,它允许一个类(称为派生类或子类)继承另一个类(称为基类或父类)的属性和方法。通过继承,派生类可以重用基类的代码,同时添加新的属性和方法或修改继承来的方法。这种机制促进了代码的复用、扩展和维护。
在面向对象编程(OOP)中,继承是一种非常重要的特性,它提供了多种理由和优势来支持其使用。
然而,也需要注意的是,过度使用继承可能会导致类层次结构变得复杂和难以维护。因此,在使用继承时应该谨慎考虑,并确保它确实是解决问题的最佳方案。在某些情况下,组合(composition)可能是比继承更好的选择。
C++中的继承是一种面向对象编程的特性,它允许我们定义一个新的类(派生类或子类)来继承另一个类(基类或父类)的属性和方法。继承的基本使用包括定义基类、定义派生类、以及通过派生类的对象来访问基类的成员。以下是C++继承的基本用法示例:
首先,我们需要定义一个基类,这个类包含了派生类将要继承的属性和方法。
class Base {
public:
void display() {
cout << "Displaying base class" << endl;
}
};
接着,我们定义一个派生类,这个类通过继承关键字(:
)来继承基类的成员。
class Derived : public Base {
public:
void show() {
cout << "Showing derived class" << endl;
}
};
注意,在上面的例子中,public
关键字指定了继承的访问级别。C++ 支持三种继承方式:public
、protected
和 private
。public
继承使得基类的 public
和 protected
成员在派生类中保持原有的访问级别。
最后,我们可以通过创建派生类的对象并使用它来访问基类和派生类的成员。
#include <iostream>
using namespace std;
// 基类和派生类的定义(如上所示)
int main() {
Derived d;
// 访问派生类自己的方法
d.show();
// 访问继承自基类的方法
d.display();
return 0;
}
::
)来明确指定访问的类。class Base {
public:
Base() { cout << "Base Constructor" << endl; }
~Base() { cout << "Base Destructor" << endl; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived Constructor" << endl; }
~Derived() { cout << "Derived Destructor" << endl; }
};
int main() {
Derived d;
// 输出顺序:Base Constructor, Derived Constructor
// 当d离开作用域时,析构函数按相反顺序调用
// 输出顺序:Derived Destructor, Base Destructor
return 0;
}
public
继承:基类的 public
和 protected
成员在派生类中保持其访问级别。protected
继承:基类的 public
和 protected
成员在派生类中变为 protected
成员。private
继承:基类的所有成员(public
、protected
和 private
)在派生类中均为 private
成员。注意,private
继承并不常用,因为它基本上隐藏了基类接口。virtual
关键字)。protected
成员在派生类中不能变为 public
)。在C++中,继承过来的权限主要取决于继承方式(public、protected、private)以及基类成员的原始访问权限(public、protected、private)
class
关键字定义类时,默认的继承方式是private;而使用struct
关键字时,默认的继承方式是public。然而,为了代码的清晰性和可维护性,建议显式指定继承方式。假设有以下基类Base
和派生类Derived
:
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
class DerivedPublic : public Base {
public:
void accessMembers() {
publicMember = 10; // 可以访问
protectedMember = 20; // 可以访问
// privateMember = 30; // 编译错误,不可访问
}
};
class DerivedProtected : protected Base {
public:
void accessMembers() {
publicMember = 10; // 可以在类内部访问,但外部不可访问
protectedMember = 20; // 可以在类内部访问,但外部不可访问
// privateMember = 30; // 编译错误,不可访问
}
};
class DerivedPrivate : private Base {
public:
void accessMembers() {
publicMember = 10; // 只能在类内部访问
protectedMember = 20; // 只能在类内部访问
// privateMember = 30; // 编译错误,不可访问
}
};
在上述示例中,不同的继承方式导致了基类成员在派生类中具有不同的访问权限。
在C++中,赋值兼容原则主要涉及指针和引用的赋值。基本规则是,派生类对象的指针或引用可以安全地赋值给基类类型的指针或引用,但反之则不然。这是因为基类指针或引用只能访问基类定义的接口,而派生类可能添加了额外的成员。
static_cast
或动态转换 dynamic_cast
)。总之,C++的继承机制提供了一种强大的方式来复用代码、表达类之间的层次关系,并实现多态性。然而,它也伴随着一些复杂的规则和限制,需要开发者仔细理解和遵守。
在C++中,当子类(派生类)和父类(基类)中存在同名成员时,这些成员实际上是在不同的作用域中定义的。这种情况主要涉及到成员函数(方法)和成员变量(属性)。处理这些同名成员时,需要特别注意作用域解析运算符(::
)的使用以及成员函数重写(Overriding)和隐藏(Hiding)的概念。
对于成员变量,如果子类定义了一个与父类同名的成员变量,那么这两个变量实际上是在不同的作用域中。子类中的同名成员变量会隐藏父类中的同名成员变量。此时,如果子类中的成员函数想要访问父类中被隐藏的成员变量,需要使用作用域解析运算符(::
)来明确指定要访问的变量属于哪个类。
class Base {
public:
int x;
};
class Derived : public Base {
public:
int x; // 隐藏了Base类中的x
void show() {
std::cout << "Derived::x = " << x << std::endl; // 访问Derived中的x
std::cout << "Base::x = " << Base::x << std::endl; // 访问Base中的x
}
};
对于成员函数,情况稍微复杂一些。成员函数存在重写(Overriding)和隐藏(Hiding)两种可能性。
class Base {
public:
virtual void func() { std::cout << "Base::func()" << std::endl; }
void anotherFunc(int) { std::cout << "Base::anotherFunc(int)" << std::endl; }
};
class Derived : public Base {
public:
void func() override { std::cout << "Derived::func()" << std::endl; } // 重写
void anotherFunc(double) { std::cout << "Derived::anotherFunc(double)" << std::endl; } // 隐藏Base::anotherFunc(int)
};
// 使用
Base* basePtr = new Derived();
basePtr->func(); // 输出Derived::func(),因为func()被重写了
basePtr->anotherFunc(1); // 输出Base::anotherFunc(int),因为Derived::anotherFunc(double)隐藏了Base::anotherFunc(int)
Derived* derivedPtr = new Derived();
derivedPtr->anotherFunc(1.0); // 输出Derived::anotherFunc(double),直接调用Derived的方法
在C++中处理父子类中的同名成员时,需要注意成员变量会被隐藏,而成员函数则可能涉及重写或隐藏。了解这些概念对于编写清晰、可维护的C++代码非常重要。
在C++中,父子类(派生类与基类)之间的静态成员、构造函数和析构函数的行为有其特定的规则和特性。
静态成员(包括静态变量和静态成员函数)属于类本身,而不是类的某个对象。因此,无论创建了多少个类的对象,静态成员都只有一份拷贝。当静态成员被定义在基类中时,这些成员也会被子类继承,但它们仍然是属于基类的,而不是子类的一个独立拷贝。
构造函数是特殊的成员函数,用于在对象创建时初始化对象。
析构函数也是特殊的成员函数,用于在对象销毁前进行清理工作。
#include <iostream>
class Base {
public:
static int staticVar;
Base() { std::cout << "Base Constructor\n"; }
~Base() { std::cout << "Base Destructor\n"; }
static void staticFunc() { std::cout << "Base Static Function\n"; }
};
int Base::staticVar = 0;
class Derived : public Base {
public:
// 注意:这里没有定义staticVar或staticFunc,所以它们从Base继承
Derived() { std::cout << "Derived Constructor\n"; }
~Derived() { std::cout << "Derived Destructor\n"; }
// 可以在这里定义同名的静态成员,但这将隐藏Base中的同名成员
};
int main() {
Derived d;
// 调用静态函数,将输出Base Static Function
Base::staticFunc();
// 访问静态变量,修改后通过Base和Derived访问都会看到修改结果
std::cout << "Before: " << Base::staticVar << std::endl;
Base::staticVar = 5;
std::cout << "After: " << Derived::staticVar << std::endl; // 注意:这里通过Derived访问也是合法的
return 0; // 析构顺序:Derived Destructor, Base Destructor
}
在这个示例中,你可以看到静态成员(staticVar
和staticFunc
)是如何被基类和派生类共享的,以及构造函数和析构函数的调用顺序。注意,由于Derived
类没有定义staticVar
或staticFunc
,所以它们是从Base
类继承的。如果Derived
类定义了同名的静态成员,那么这些成员将隐藏基类中的同名成员。