C++继承

继承

继承就是新类从已有类那里得到已有的特性。

类的派生指的是从已有类产生新类的过程。

原有的类成为基类或父类,产生的新类称为派生类或子类。

// 基类
class Animal {
    // eat() 函数
    // sleep() 函数
};
// 派生类
class Dog : public Animal {
    // bark() 函数
};

如果不希望一个类被继承,也可以使用 final 关键字。

格式如下:

class Class_name final
{
    ...
};

则该类将不能被继承。

单继承/多继承

定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:

// 单继承
class <派生类名> : <继承方式> <基类名> {
	<派生类类体>
};
// 多继承
class <派生类名> : <继承方式1> <基类名1>, <继承方式2> <基类名2>, ... {
	<派生类类体>
};

<继承方式1>即访问修饰符 access-specifier ,是 public、protectedprivate 之一,缺省默认为 private。

成员变量和成员函数不写访问修饰符时,默认也是 private。

多继承二义性例子

class A {
public:
	void z() {
		cout << "Az" << endl;
	}
};

class B {
public:
	void z() {
		cout << "Bz" << endl;
	}
};

class C: public A, public B {
};

int main() {
    C c1;
    //c1.z(); // [Error] request for member 'z' is ambiguous;candidates are: void B::z();void A::z() 
    // 通过指定成员名来限定域,消除二义性
    c1.A::z();
    c1.B::z();
    return 0;
}

一个派生类继承了所有的基类方法,除了以下的方法:

  • 基类的构造函数、析构函数和拷贝构造函数。
  • 基类的重载运算符。
  • 基类的友元函数。

转换与继承

  • 派生类对象可以赋值或初始化基类对象。

  • 基类对象不可以赋值给派生类对象。

  • 基类对象的指针和引用可以指向派生类对象。

  • 派生类对象的指针和引用不能指向基类对象,但是可以通过强制转化完成。

继承与静态成员

  • 基类定义了static成员,则整个继承体系里面只有一个这样的成员。**无论派生出多少个子类,都只有一个static成员实例。**所有类对象共享一个static类成员,static类对象必须要在类外进行初始化。

  • static成员遵循常规访问控制与继承

继承与友元关系

  • 友元关系不能被继承,即友元无法访问基类的私有成员与被保护成员

隐藏

在继承体系中基类和派生类都有独立的作用域。若在派生类和基类中有同名成员(不管是变量还是函数),基类将隐藏对父类的同名成员的访问,这种情况称为同名隐藏或者重定义。就是在派生类中使用该成员(包括在定义派生类时使用,也包括通过派生类对象访问该成员)时,实际上使用的是派生类新增的成员,而不是从基类继承来的。

隐藏例子

class A {
public:
	const static int aa = 3; 
	void z() {
		cout << "Az" << endl;
	}
	void x() {
		cout << "Ax" << endl;
	}
};

class C: public A {
public:
	const static int aa = 5; 
	void z() {
		cout << "Cz" << endl;
	}
	void x(int a) {
		cout << "Cx" << endl;
	}
};

int main() {
    C c1;
    cout << c1.aa << endl; // 5,C类隐藏了A类的同名成员变量aa
	cout << c1.A::aa << endl; // 3,指定成员名获取被隐藏的成员变量 
    c1.z(); // Cz
    c1.A::z(); // Az
    c1.x(); // 编译失败,因为调用的是C类的方法x,即,只要同名,哪怕参数不同也能构成隐藏 
    return 0;
}

访问控制

访问方式publicprotectedprivate
同一个类和友元类内部可以可以可以
派生类内部可以可以不可以
外部类可以不可以不可以
继承方式public继承protected继承private继承
基类的public成员仍为public成员变为protected成员变为private成员
基类的protected成员仍为protected成员仍为protected成员变为private成员
基类的private成员不可见不可见不可见

外部类其实就是指能不能通过对象来访问。所以只有public的成员可以通过对象来访问。

继承方式中的 public、protected、private 是用来指明基类成员在派生类中的最高访问权限的。

**基类的 private 成员不能在派生类中使用,不代表基类的 private 成员不能被继承。**实际上,基类的 private 成员是能够被继承的,并且(成员变量)会占用派生类对象的内存,它只是在派生类中不可见,导致无法使用罢了。

访问控制例子

class Animal {
public:
    void eat() {
        cout << "animal eat" << endl;
    }
protected:
	void sleep() {
		cout << "animal sleep" << endl;
	}
private:
    void breathe() {
        cout << "animal breathe" << endl;
    }
};
class Fish : public Animal {
public:
    void test() {
        eat();       // 此时eat()的访问权限为public,派生类内部能够访问
        sleep();     // 此时sleep()的访问权限为protected,派生类内部能够访问
        breathe();   // 此时breathe()的访问权限为no access,派生类内部不能够访问
    }
};

int main(void) {
    Fish f;
    f.eat();          // 此时eat()的访问权限为public,在类外部能够访问
    f.sleep();        // 此时sleep()的访问权限为protected,在类外部不能够访问
    f.breathe();      // 此时breathe()的访问权限为no access,在类外部不能够访问
    return 0;
}

修改访问权限

使用 using 关键字可以改变基类成员在派生类中的访问权限,例如将 public 改为 private、将 protected 改为 public。

注意:using 只能改变基类中 public 和 protected 成员的访问权限,不能改变 private 成员的访问权限,因为基类中 private 成员在派生类中是不可见的,根本不能使用,所以基类中的 private 成员在派生类中无论如何都不能访问。

class A {
public:
	void z() {
		cout << "Az" << endl;
	}
protected:
	int a1;
	int b1; 
};

class C: public A {
public:
	using A::a1; // 将a1的访问权限改为public
private:
	using A::z; // 将z的访问权限改为private
};

int main() {
	A a1;
	a1.z();
    C c1;
    c1.a1 = 6;
    cout << c1.a1 << endl; // 6,修改成public成功,类外可以访问
    //c1.b1 = 6; // 设置失败,是protected权限 
    //c1.z(); // 设置失败,是private权限 
    return 0;
}

环状继承/菱形继承

class D{......};
class B: public D{......};
class A: public D{......};
class C: public B, public A{.....};

这个继承会使D创建两个对象,虽然不会报错但是会造成二义性,要解决就要用虚继承

格式:class 类名: virtual 继承方式 父类名

虚继承在创建对象的时候会创建一个虚表

class D{......};
class B: virtual public D{......};
class A: virtual public D{......};
class C: public B, public A{.....};

菱形继承例子

class D {
public:
    D() {cout << "D()" << endl;}
    ~D() {cout << "~D()" << endl;}
protected:
    int d;
};

class B: virtual public D
{
public:
    B() {cout << "B()" << endl;}
    ~B() {cout << "~B()" << endl;}
protected:
    int b;
};

class A: virtual public D
{
public:
    A() {cout << "A()" << endl;}
    ~A() {cout << "~A()" << endl;}
protected:
    int a;
};

class C: public B, public A
{
public:
    C() {cout << "C()" << endl;}
    ~C() {cout << "~C()" << endl;}
protected:
    int c;
};

int main() {
    C c;   // 构造函数,DBDAC,加了virtual后是DBAC
    cout << sizeof(c) << endl; // 20,加了virtual后是40
    // 析构函数,CADBD,加了virtual后是CABD
    return 0;
}

构造函数与析构函数的调用

1、构造派生类对象:
a.调用派生类的构造函数;
b.在派生类构造函数初始化列表处调用基类的构造函数;
c.完成基类构造函数;
d.返回并完成派生类构造函数剩下部分

2、析构派生类对象:
a.调用派生类的析构函数;
b.执行派生类析构函数的函数体;
c.调用基类的析构函数;
d.执行基类析构函数函数体后返回

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值