继承允许我们创建一个新的类(子类),它继承另一个类(父类)的属性和方法。这使得代码重用和扩展变得更加容易。
在Java中,子类可以继承父类的构造方法。当子类创建对象时,会自动调用父类的构造方法来初始化从父类继承的成员变量和方法。子类可以通过super关键字来调用父类的构造方法。
以下是一个示例代码:
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public void speak() {
System.out.println("Animal speaking");
}
}
public class Dog extends Animal {
private int age;
public Dog(String name, int age) {
super(name); // 调用父类的构造方法
this.age = age;
}
public void speak() {
System.out.println("Dog speaking");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("Tommy", 3);
dog.speak(); // 输出:Dog speaking
}
}
在上面的例子中,Animal类有一个带有一个参数的构造方法,它接受一个name(名字)参数并将其赋值给name成员变量。Dog类继承Animal类,并新增了一个age(年龄)成员变量。Dog类的构造方法使用super关键字调用父类的构造方法来初始化name成员变量。最后,在Main类中创建Dog对象,输出结果为"Dog speaking"。
Python要让子类继承父类的所有方法和属性,包括含参数的__init__
方法,可以使用super()
函数来实现。
下面是一个示例代码:
class Parent:
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
def method1(self):
print("Parent method1")
def method2(self):
print("Parent method2")
class Child(Parent):
def __init__(self, arg1, arg2, arg3):
super().__init__(arg1, arg2) # 调用父类的__init__方法
self.arg3 = arg3
def method3(self):
print("Child method3")
# 创建子类对象
child = Child("arg1 value", "arg2 value", "arg3 value")
# 调用继承的父类方法
child.method1() # 输出:Parent method1
child.method2() # 输出:Parent method2
child.method3() # 输出:Child method3
在子类的__init__
方法中使用super().__init__()
调用父类的__init__
方法,可以将父类的初始化代码复用。
在C++例子中,Cat
类继承自Animal
类,因此Cat
对象可以调用eat
方法(从Animal
继承而来),同时也可以调用Speak
方法(Cat
类特有的)。
class Animal {
public:
void eat() {
cout << "The animal eats." << endl;
}
};
class Cat : public Animal { // Dog继承自Animal
public:
void Speak() {
cout << "喵!" << endl;
}
};
int main() {
Cat myCat;
myCat.eat(); // 继承自Animal的方法
myCat.Speak(); // Cat特有的方法
return 0;
}
1 私有继承和公有继承
C++中类继承的两种主要方式,它们之间存在几个关键的区别:
-
成员访问权限:
-
公有继承(public inheritance):在公有继承中,基类的公有(public)成员在派生类中仍然保持公有成员的地位。这意味着派生类的对象可以直接访问这些公有成员。同时,基类的保护(protected)成员在派生类中变为保护成员,即派生类的成员函数和友元可以访问它们,但派生类的对象不可以。基类的私有(private)成员在派生类中仍然是不可访问的。
-
私有继承(private inheritance):在私有继承中,基类的公有和保护成员在派生类中都会变为私有成员。这意味着只有派生类的成员函数和友元可以访问这些成员,派生类的对象以及派生类之外的任何代码都不能直接访问它们。基类的私有成员在派生类中仍然是不可访问的。
-
-
用途和含义:
-
公有继承:通常用于表示“是一种”(is-a)的关系。例如,如果有一个
Animal
类和一个从Animal
公有继承的Dog
类,那么可以说“一个狗是一种动物”。公有继承意味着派生类继承了基类的接口(即公有成员),并且派生类的对象可以像基类对象一样使用。 -
私有继承:用于表示实现细节或者“拥有”(has-a)的关系。私有继承允许派生类使用基类的实现,但不需要暴露基类的接口给派生类的对象或外部代码。这通常用于实现代码复用,而不是创建新的接口。
-
-
多态行为:
-
在公有继承中,如果基类有一个虚函数,并且派生类重写了这个函数,那么通过基类指针或引用调用这个函数时,会调用派生类的版本,实现运行时多态。
-
在私有继承中,虽然派生类可以重写基类的虚函数,但由于基类成员在派生类中变为私有,外部代码无法通过基类指针或引用调用派生类的版本。因此,私有继承通常不用于实现多态行为。
-
-
设计原则:
-
使用公有继承时,应该仔细考虑是否确实需要继承基类的接口。如果派生类不需要暴露基类的所有公有成员,或者如果基类的公有成员与派生类的概念不符,那么使用公有继承可能不是最佳选择。
-
使用私有继承时,应该明确这是为了代码复用而不是为了创建新的接口。私有继承通常
2 关于虚继承(Virtual Inheritance)
是C++中的一种机制,用于解决多重继承中可能出现的菱形继承问题。在菱形继承中,一个派生类从多个基类继承,而这些基类又共同继承自同一个更基础的类。如果没有使用虚继承,派生类中将包含多个基础类的子对象,这可能导致数据冗余、资源浪费以及歧义。
用于实现细节,而不是用于定义类的外部行为。
3 关于菱形继承
菱形继承(Diamond Inheritance)是面向对象编程中一个常见的概念,特别是在C++中,它涉及到多重继承的复杂性。在菱形继承中,一个派生类从两个基类继承,而这两个基类又共同从一个更基础的类继承。这种结构形成了一个类似于菱形的继承关系图。
下面是一个简单的例子来说明菱形继承:
在这个例子中,
MostDerived
类从Derived1
和Derived2
两个类继承,而Derived1
和Derived2
又都从Base
类继承。因此,MostDerived
类实际上间接地从Base
类继承了两次,形成了菱形继承。class Base { public: void foo() { std::cout << "Base::foo()" << std::endl; } }; class Derived1 : public Base { public: // 可能有一些特有的成员函数或数据 }; class Derived2 : public Base { public: // 可能有一些特有的成员函数或数据 }; class MostDerived : public Derived1, public Derived2 { public: // MostDerived 类特有的成员函数或数据 };
菱形继承可能会导致一些问题,特别是当涉及到虚函数和虚继承时。在上面的例子中,如果没有特别处理,
MostDerived
类中将会有两个Base
类的子对象(即Derived1
和Derived2
各包含一个Base
子对象),这可能导致资源重复和歧义。例如,如果Base
类有一个数据成员,那么MostDerived
类将有两个这样的数据成员,一个来自Derived1
,另一个来自Derived2
。为了解决这个问题,C++提供了虚继承(Virtual Inheritance)的概念。通过在基类继承时使用
virtual
关键字,可以确保无论基类被继承多少次,在派生类中只会有一个基类子对象的实例。修改上面的例子以使用虚继承:
在这个修改后的例子中,
Derived1
和Derived2
都以虚方式从Base
类继承,因此MostDerived
类中只会有一个Base
类的子对象实例,避免了资源重复和歧义。class Base { public: void foo() { std::cout << "Base::foo()" << std::endl; } }; class Derived1 : virtual public Base { // 注意这里的 virtual 关键字 public: // ... }; class Derived2 : virtual public Base { // 注意这里的 virtual 关键字 public: // ... }; class MostDerived : public Derived1, public Derived2 { public: // ... };
使用虚继承需要谨慎,因为它可能引入额外的运行时开销,并可能使类的内存布局变得复杂。因此,只有在确实需要解决菱形继承问题时才应该使用虚继承。在大多数情况下,通过仔细设计类结构和使用组合而不是继承,可以避免菱形继承的问题。
-