c++-面向对象-继承

例子

#include <iostream>
using namespace std;

class People
{
public:
    void setname(char *name);
    void setage(int age);
    char *getname();
    int getage();
private:
    char *name;
    int  age;
};

void People::setname(char *name)
{
    this->name=name;
}

void People::setage(int age)
{
    this->age=age;
}

char* People::getname()
{
    return this->name;
}

int People::getage()
{
    return this->age;
}

// 继承
class Student:public People
{
public:
    void setscore(float score);
    float getscore();
private:
    float score;
};

void Student::setscore(float score)
{
    this->score=score;
}

float Student::getscore()
{
    return this->score;
}

int main()
{

    Student stu;
    stu.setname("zhangsan");
    stu.setage(10);
    stu.setscore(90.f);
    cout<<stu.getname()<<" "<<stu.getage()<<" "<<stu.getscore()<<endl;

    return 0;
}

继承方式

class Son : [继承方式] Parent

不写继承方式默认是private私有继承

继承方式限定了基类成员在派生类中的访问权限

通常来说,一般使用public继承,使得前后的一致性

protected成员

protected成员在本类中不能通过对象访问
当存在继承关系时

基类中的protected成员可以在派生类中使用
当需要基类的成员不向外暴露(不能通过对象访问),还能再派生类中使用,只能声明为protected

private成员

基类的private成员不能被使用,但是是可以被继承的,并且成员变量会占用派生类对象的内存,只是在派生类中不可见,导致无法使用

public公有继承

基类中所有public成员在派生类中为public属性
基类中所有protected成员在派生类中为protected属性
基类中的所有private成员在派生类中不能使用

private私有继承

基类中所有public成员在派生类中为private属性
基类中所有protected成员在派生类中为private属性
基类中的所有private成员在派生类中不能使用

protected保护继承

基类中所有public成员在派生类中为protected属性
基类中所有protected成员在派生类中为protected属性
基类中的所有private成员在派生类中不能使用

例子

#include <iostream>
using namespace std;

class People
{
public:
    void setname(char *name);
    void setage(int age);
    char *getname();
    int getage();
    void setsex(char *sex);
    char *getsex();
protected:
    char *name;
    int age;
private:
    char *sex;
};

void People::setname(char *name)
{
    this->name=name;
}

void People::setage(int age)
{
    this->age=age;
}

char* People::getname()
{
    return this->name;
}

int People::getage()
{
    return this->age;
}

void People::setsex(char *sex)
{
    this->sex=sex;
}

char *People::getsex()
{
    return this->sex;
}


// 继承自people
class Student:public People
{
public:
    void setscore(float score);
    float getscore();
// private:
protected:
    float score;
};

void Student::setscore(float score)
{
    this->score=score;
}

float Student::getscore()
{
    return this->score;
}

// 继承自student
class Son:public Student
{
private:
    int grade;
public:
    void setGrade(int grade);
    void display();
};

void Son::setGrade(int grade)
{
    this->grade=grade;
}

void Son::display()
{
    // People::name protected ->protected
    cout<<this->name<<endl;
    // People::age protected -> protected
    cout<<this->age<<endl;
    // Son grade
    cout<<this->grade<<endl;
    // Student::score protected -> protected
    cout<<this->score<<endl;
    // People::sex private -> 不可见
    // People::getsex() public -> public
    cout<<this->getsex()<<endl;
}

int main()
{

    Student stu;
    stu.setname("zhangsan");
    stu.setage(10);
    stu.setscore(90.f);
    cout<<stu.getname()<<" "<<stu.getage()<<" "<<stu.getscore()<<endl;

    Son son;
    // People::setname() public -> public
    son.setname("lisi");
    // People::setage() public -> public
    son.setage(20);
    //Student::setsocre() public -> public
    son.setscore(80.f);
    // son.setgrade()
    son.setGrade(2);
    // People::setsex() public -> public
    son.setsex("male");
    
    son.display();

    return 0;
}

改变访问权限 (改变继承内的访问权限)

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

class People
{
public:
    void setname(char *name);
    void setage(int age);
    char *getname();
    int getage();
    void print();
protected:
    char *name;
    int age;
};

void People::setname(char *name)
{
    this->name=name;
}

void People::setage(int age)
{
    this->age=age;
}

char* People::getname()
{
    return this->name;
}

int People::getage()
{
    return this->age;
}

void People::print()
{
    cout<<this->name<<" "<<this->age<<endl;
}

// 继承自people
class Student:public People
{
public:
    using People::name; // protected -> public
    using People::age; // protected -> public

    void setscore(float score);
    float getscore();

    void print_student();

// private:
protected:
    float score;
private:
    using People::print; // public ->private
};

void Student::setscore(float score)
{
    this->score=score;
}

float Student::getscore()
{
    return this->score;
}

void Student::print_student()
{
    /*
    public:
        using People::name; // protected -> public
        using People::age; // protected -> public
    */
    cout<<this->name<<endl;
    cout<<this->age<<endl;

    cout<<this->score<<endl;

    cout<<"*****"<<endl;
    this->print();

}

指针/改变对象访问权限 – 还没看

普通实例对象不能通过对象访问protected、private成员

不建议使用

继承作用域

基类为外层作用域
派生类为内存作用域

继承覆盖(重命名)

派生类成员中(包括成员变量和成员函数)和基类中的成员重名,那么会覆盖从基类继承过来的成员

使用基类的成员函数: stu.People::print()

class People
{
protected:
    char *name;
    int age;
public:
    void print();
};

void People::print()
{
    cout<<this->name<<endl;
    cout<<this->age<<endl;
}

class Student:public People
{
private:
    float score;
public:
    Student(char *name,int age,float score);
    void print();
};

Student::Student(char *name,int age,float score)
{
    this->name=name;
    this->age=age;
    this->score=score;
}
void Student::print()
{
    cout<<this->name<<" "<<this->age<<" "<<this->score<<endl;
}

int main()
{
    Student stu("zhangsan",10,2.f);
    stu.print();

    // 使用基类的成员函数
    stu.People::print();

    return 0;
}

覆盖与重载

派生类和基类的同名函数,只能是覆盖不能是重载
要想使用同名函数就要指明类的作用域,(实际上根据指明的继承方式,是可以继承的,但是需要指明类的作用域) – 见下面的多继承

继承内存模型 – 还没看

类的实例对象的内存模型

类的声明与定义不占用内存空间
类对象占用内存空间

编译器将成员变量、成员函数分开存储:分别对每个实例对象的成员变量分配内存,但是所有对象都共享同一段函数代码
成员变量在堆、栈区份分配内存
成员函数在代码区分配内存

继承进阶

继承下的构造函数

类的构造函数不能被继承(直观上来讲 基类和派生类的构造函数名都不同)

派生类的构造函数调用基类的构造函数
派生类构造函数总是先调用基类构造函数再执行其他,且必须调用基类的构造函数
基类构造函数的调用只能在参数列表中被初始化(如果是在代码块域内,实际上是将其看做普通函数调用,但是构造函数不会被继承,所以不能被调用)


#include <iostream>
using namespace std;

class People
{
protected:
    // char *m_name;
    // int m_age;

    char *name;
    int age;
public:
    People(char *name,int age);
};

People::People(char *name,int age)
    // :m_name(name),m_age(age)
{
    this->name=name;
    this->age=age;
}

class Student:public People
{
private:
    float score;
public:
    Student(char *name,int age,float score);
    void print();
};

Student::Student(char *name,int age,float score)
    :People(name,age)//,m_score(score)
{
    // this->name=name;
    // this->age=age;
    this->score=score;
}

void Student::print()
{
    cout<<this->name<<endl;
    cout<<this->age<<endl;
    cout<<this->score<<endl;
}

int main()
{
    Student stu("zhangsan",10,2.f);
    stu.print();
    return 0;
}

构造函数调用

基类构造函数总是被优先调用
派生类的构造函数中只能调用直接基类的构造函数,不能调用间接基类的构造函数

通过派生类创建对象时必须调用基类的构造函数,
定义派生类构造函数时最好指明基类构造函数,如果不指明,就调用基类的默认构造函数(不带参数的构造函数),如果没有默认构造函数,那么编译失败

即使没有显示的调用构造函数,实际上仍然会调用基类、派生类的构造函数

继承下的析构函数

析构函数不会被继承
派生类的析构函数中不用显示的调用基类的析构函数,(每个类只有一个析构函数)
销毁派生类对象时,先执行派生类的析构函数,再执行基类析构函数

class A
{
public:
    A();
    ~A();
};

A::A()
{
    cout<<"A 构造"<<endl;
}

A::~A()
{
    cout<<"A 析构"<<endl;
}

class B:public A
{
public:
    B();
    ~B();
};

B::B()
{
    cout<<"B 构造"<<endl;
}

B::~B()
{
    cout<<"B 析构"<<endl;
}

class C:public B
{
public:
    C();
    ~C();
};

C::C()
{
    cout<<"C 构造"<<endl;
}

C::~C()
{
    cout<<"C 析构"<<endl;
}

int main()
{
    /*
        A 构造
        B 构造
        C 构造
        C 析构
        B 析构
        A 析构
    */
    C c;

    return 0;
}

多继承 – 不建议使用

单继承:派生类只有一个基类
多继承:派生类有多个基类

多继承中的继承方式可以是任意的

class D:public A,private B,protected C
{

};

多继承下的构造函数

多继承下,基类构造函数的调用顺序和在派生类构造函数中出现的顺序无关,而是和声明派生类时基类出现的顺序相同

与参数列表的次序机制类似,(成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,只与成员变量在类中声明的顺序有关)

class A {};
class B {};
class C {}class D:public B,private C,protected A {};

// 构造函数的效用顺序依然是 A、B、C、D

例子

顺序继承(不是菱形继承)

class A
{
protected:
    int classA_val_A;
    int classA_val_B;
public:
    A(int a,int b);
    ~A();

    void print();
};

A::A(int a,int b)
{
    this->classA_val_A=a;
    this->classA_val_B=b;
    cout<<"base a 构造"<<endl;
}

A::~A()
{
    cout<<"base a 析构"<<endl;
}

void A::print()
{
    cout<<this->classA_val_A<<endl;
    cout<<this->classA_val_B<<endl;
}

class B
{
protected:
    int classB_val_A;
    int classB_val_B;
public:
    B(int c,int d);
    ~B();

    void print();
};

B::B(int c,int d)
{
    this->classB_val_A=c;
    this->classB_val_B=d;
    cout<<"base b 构造"<<endl;
}

B::~B()
{
    cout<<"base b 析构"<<endl;
}

void B::print()
{
    cout<<this->classB_val_A<<endl;
    cout<<this->classB_val_B<<endl;
}

class C:public A,public B
{
private:
    int classC_val_A;
public:
    C(int a,int b,int c,int d,int e);
    ~C();

    void print();
};

C::C(int a,int b,int c,int d,int e)
    :A(a,b),B(c,d)
{
    this->classC_val_A=e;
    cout<<"class c 构造"<<endl;
}

C::~C()
{
    cout<<"class c 析构"<<endl;
}

void C::print()
{
    cout<<this->classA_val_A<<endl;
    cout<<this->classA_val_B<<endl;
    cout<<this->classB_val_A<<endl;
    cout<<this->classB_val_B<<endl;
    cout<<this->classC_val_A<<endl;

    cout<<"**********"<<endl;
    this->A::print();
    cout<<"**********"<<endl;
    this->B::print();
}


int main()
{
    C instance_c(1,2,3,4,5);
    instance_c.print();
    /*
        base a 构造
        base b 构造
        class c 构造
        1
        2
        3
        4
        5
        class c 析构
        base b 析构
        base a 析构
    */

    return 0;
}

虚继承 – 不建议使用

多继承容易产生命名冲突,解决的是菱形继承的问题
解决方法: 虚继承 virtual 继承方式
加上virtual 会将重复的变量仅保存一份成员变量,(从而不会造成歧义)

虚继承,是让某个类做出声明,表明可以共享该类的基类,这个被共享的基类为虚基类
无论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员

#include <iostream>
using namespace std;

class A // A是一个虚基类
{
protected:
    int classA_a;
};

class B:virtual public A
{
protected:
    int classB_b;
};

class C:virtual public A
{
protected:
    int classC_c;
};

class D:public B,public C
{
private:
    int classD_d;
public:
    void seta(int a);
    void setb(int b);
    void setc(int c);
    void setd(int d);
};

/*
    如果不加上virtual 则需要使用
    void D::seta(int a){this->B::classA_a=a;}
    void D::seta(int a){this->C::classA_a=a;}
*/
void D::seta(int a){this->classA_a=a;}
void D::setb(int b){this->classB_b=b;}
void D::setc(int c){this->classC_c=c;}
void D::setd(int d){this->classD_d=d;}

int main(int argc, char const *argv[])
{
    D instance_d;
    instance_d.seta(1);
    instance_d.setb(2);
    instance_d.setc(3);
    instance_d.setd(4);
    return 0;
}   

虚继承的覆盖(二义性)

菱形继承中,在A中定义了一个x成员变量

如果B、C中都没有定义x,那么x会被解析成A的成员,此时不存在二义性
如果B或C其中任意一个类中定义了x,也不会有二义性,派生类的x比虚基类的优先级高
如果B和C中都定义了x,那么直接访问x仍然将产生二义性

/*
    A
   / \
  B   C
   \ /
    D
*/

虚继承下的构造函数

最终派生类的构造函数必须调用虚基类的构造函数
对于派生类来说,虚基类是间接基类,而不是直接基类
与普通继承不同,普通继承中,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的

如果有直接派生类初始化虚基类,那么在调用虚基类的构造函数是可能给出不同的实参,这会造成编译器的模糊(二义性),不知道使用哪个实参进行初始化
所以无论如何利用直接虚派生类进行初始化,都会根据最终的派生类进行初始化虚基类
在调用构造函数的调用顺序上,都是先调用虚基类的构造函数

#include <iostream>
using namespace std;

class A
{
protected:
    int classA_a;
public:
    A(int a);
};
A::A(int a)
{
    classA_a=a;
}

class B:virtual public A
{
protected:
    int classB_b;
public:
    B(int a,int b);
    void print();
};
B::B(int a,int b)
    :A(a)
{
    this->classB_b=b;
}
void B::print()
{
    cout<<this->classA_a<<endl;
    cout<<this->classB_b<<endl;
}

class C:virtual public A
{
protected:
    int classC_c;
public:
    C(int a,int c);
    void print();
};
C::C(int a,int c)
    :A(a)
{
    this->classC_c=c;
}
void C::print()
{
    cout<<this->classA_a<<endl;
    cout<<this->classC_c<<endl;
}

class D:public B,public C
{
private:
    int classD_d;
public:
    D(int a,int b,int c,int d);

    void seta(int a);
    void setb(int b);
    void setc(int c);
    void setd(int d);

    void print();
};
D::D(int a,int b,int c,int d)
    :A(a),B(a,b),C(a,c)
{
    this->classD_d=d;
}
void D::print()
{
    cout<<this->classA_a<<endl;
    cout<<this->classB_b<<endl;
    cout<<this->classC_c<<endl;
    cout<<this->classD_d<<endl;
}

/*
    如果不加上virtual 则需要使用
    void D::seta(int a){this->B::classA_a=a;}
    void D::seta(int a){this->C::classA_a=a;}
*/
void D::seta(int a){this->classA_a=a;}
void D::setb(int b){this->classB_b=b;}
void D::setc(int c){this->classC_c=c;}
void D::setd(int d){this->classD_d=d;}

int main(int argc, char const *argv[])
{
    // D instance_d;
    // instance_d.seta(1);
    // instance_d.setb(2);
    // instance_d.setc(3);
    // instance_d.setd(4);
    
    B instance_b(1,2);
    instance_b.print();

    C instance_c(3,4);
    instance_c.print();

    D instance_d(5,6,7,8);
    instance_d.print();
    
    return 0;
}

派生类与基类的赋值

可以将派生类的实例对象 赋值 给基类的实例对象
可以将派生类的实例对象指针 赋值 给基类的实例对象指针
可以将派生类的实例对象的引用 赋值 给基类的实例对象的引用
基类不能想派生类转换

只能用派生类实例对象赋值给基类实例对象赋值,而不能用基类实例对象给派生类实例对象赋值

基类实例对象(指针)=派生类实例对象(指针)

赋值的本质是将现有的数据写入已分配的内存中,对象的内存值包含了成员变量,所以对象之间的赋值是成员变量的赋值,成员函数不存在赋值问题
派生类赋值给基类对象后,基类实例对象不会给派生类的成员,所以不能通过基类对象来访问派生类的成员

赋值的机制: 编译器通过指针来访问成员变量,指针指向哪个对象就使用哪个对象的数据;编译器通过指针的类型来访问成员函数,指针属于哪个类的类型就使用哪个类的函数

注意
当父类指针指向派生类对象时,只能访问派生类对象从父类继承而来的成员变量和函数,而不能访问派生类对象独有的成员变量和函数。如果需要访问派生类对象独有的成员变量和函数,必须将父类指针转换为派生类指针或者引用。需要注意的是,这种转换只有在派生类对象的实际类型是该派生类时才是安全的,否则会导致未定义的行为。

/*
    A   C
    |   |
    B   |
    \   /
      D
*/
```cpp
#include <iostream>
using namespace std;

class A
{
protected:
    int classA_val_a;
public:
    A(int a);
    void print();
};

A::A(int a)
{
    this->classA_val_a=a;
}

void A::print()
{
    cout<<this->classA_val_a<<endl;
}

class B:public A
{
protected:
    int classB_val_b;
public:
    B(int a,int b);
    void print();
};

B::B(int a,int b)
    :A(a)
{
    this->classB_val_b=b;
}
void B::print()
{
    cout<<this->classA_val_a<<endl;
    cout<<this->classB_val_b<<endl;
}

class C
{
protected:
    int classC_val_c;
public:
    C(int c);
    void print();
};

C::C(int c)
{
    this->classC_val_c=c;
}

void C::print()
{
    cout<<this->classC_val_c<<endl;
}

class D:public B,public C
{
private:
    int classD_val_d;
public:
    D(int a,int b,int c,int d);
    void print();
};

D::D(int a,int b,int c,int d)
    :B(a,b),C(c)
{
    this->classD_val_d=d;
}

void D::print()
{
    cout<<this->classA_val_a<<endl;
    cout<<this->classB_val_b<<endl;
    cout<<this->classC_val_c<<endl;
    cout<<this->classD_val_d<<endl;
}

int main()
{

    /* 例子3 */
    A a(10);
    B b(11,12);
    a.print();
    b.print();
    cout<<"========"<<endl;
    a=b;
    a.print();
    b.print();
    cout<<"========"<<endl;

    /* 例子2 */
    A *pa=new A(1);
    B *pb=new B(2,3);
    C *pc=new C(4);
    D *pd=new D(5,6,7,8);

    pa->print();
    pb->print();
    pc->print();
    pd->print();

    cout<<"*******"<<endl;
    pa=pb;
    pa->print(); // 2
    cout<<"*******"<<endl;
    pb=pd;
    pb->print(); // 5 6
    cout<<"*******"<<endl;
    pc=pd;
    pc->print(); // 7
    cout<<"*******"<<endl;
    cout<<pa<<endl;
    cout<<pb<<endl;
    cout<<pc<<endl;
    cout<<pd<<endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值