C++继承与组合

//面向对象的三大特性:封装、继承、多态
//迭代器的设计就是封装,有可能是原生指针,有可能是类封装的指针,屏蔽了底层的复杂细节
//继承的概念以及定义:
// 继承是面向对象程序设计使代码可以复用的最重要手段;
// 允许在在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量)

class person//父类/基类
{
public:
    void identity()
    {
        //对父类的私有成员的间接实用
        cout << _address << endl;
    }
protected:
    char _name;
    int _age;
    //父类的private无论以何种方式继承都对子类无法使用,但是已经完成了继承
private:
    char _address;
};

#define CONTAINER std::vector//这里要能够理解宏的替换本质

//类模版继承
namespace wjl
{
    template<class T>
    class stack : public CONTAINER<T>
    {
    public:
        void push(const T& x)
        {
            CONTAINER<T>::push_back(x);
        }

        void pop()
        {
            CONTAINER<T>::pop_back();
        }

        int size()
        {
            return CONTAINER::size();
        }

        const T& top()
        {
            return CONTAINER<T>::top();
        }

        bool empty()
        {
            return CONTAINER<T>::empty();
        }

        CONTAINER<T>::iterator begin()
        {
            return CONTAINER<T>::begin();
        }

        CONTAINER<T>::iterator end()
        {
            return CONTAINER<T>::end();
        }
    private:
    };
}

class student :public person//子类/派生类
{
public:
    void study()
    {
        cout << _name << endl;
        //cout << _address << endl;
    }
protected:
    int stuid;
};



//父类和子类对象赋值兼容转换
// 在public继承的前提下 : 继承的子类对象可以赋值给父类的对象、指针或引用,
// 这里有个形象的说法是切割,只能子类给父类,不能父类给子类
//把子类中父类的部分切片出来,再拷贝给父类

//继承中的作用域:
// 1、子类和分类都有自己的作用域
// 2、当子类和父类有同名的成员变量时,子类会限制父类对同类成员变量的访问,这叫做隐藏
// 3、对于成员函数,只要函数名相同,就构成隐藏关系(重载要求在同一作用域中)
//        ——派生类隐藏了父类,所以不回去父类查找同名函数,除非指定作用域
// 4、尽量不要定义成员函数

//子类的默认成员函数
//    四个常见默认成员函数:(构造、拷贝构造、赋值重载、析构)
//        1、子类的构造函数必须调用父类的构造函数构造父类的部分,如果父类没有默认构造函数
//    必须在子类初始化列表中显式调用,不允许子类直接初始化父类成员变量,要把父类当做一个整体
//    像定义匿名对象一样调用(例如Person(name))
//        2、析构函数不需要显式调用父类的析构函数,因为编译器会自动调用

class Person
{
public:
    Person(const char* name, int age, const char* sex)
        :_name(name)
        ,_age(age)
        ,_sex(sex)
    {

    }

    Person& operator=(const Person& p)
    {
        if (&p == this)
        {
            return *this;
        }
        _name = p._name;
        _sex = p._sex;
        _age = p._age;

        return *this;
    }

    //int _num = 111;
    string _name;
    int _age;
    string _sex;
};

class Student : public Person
{
public:
    Student(const char* name, int age, const char* sex, int No)
        : Person(name, age, sex)//显式写父类构造的方法,类似于写匿名对象
        , _No(No)
    {
        //严格来说student自己的拷贝构造默认生成就够用了
        //但有资源需要释放的时候就需要自己去写
    }

    //拷贝构造
    Student(const Student& s)
        : _No(s._No)
        //, _ptr(nullptr)
        , Person(s)//赋值兼容转换
    {
        //...
    }

    //赋值运算符重载
    Student& operator=(const Student& s)
    {
        if (this != &s)
        {
            //这里与父类的operator形成了隐藏关系,所以只有指定了类域才能调用
            Person::operator=(s);//对父类的成员使用父类的运算符重载
            _No = s._No;
        }
        return *this;
    }

    //析构函数——严格来说默认就够
    ~Student()
    {
        //子类的析构和父类的析构构成隐藏关系,析构函数都会被处理成destructor
        //不需要显式调用,子类析构函数之后,会自动调用父类析构
        // ——为了保证析构顺序是先子后父
        //Person::~Person();
        _No = 0;
    }

    //int _num = 222;
    int _No;//学号
    //* _ptr;
};
//这里的两个test1函数构成隐藏关系,对于成员函数只要函数名相同,就构成隐藏
//函数重载要求在同一作用域,而父类和子类都有自己的作用域,因此两个函数 无法构成重载


//int main()
//{
//    Student sobj;
//    //子类对象可以赋值给父类对象、指针或引用
//    Person pobj = sobj;//这里并没有类型转换,也没有产生临时变量,类比下面的i和d
//    Person* pp = &sobj;
//    Person& rp = sobj;
//    rp._name = "张三";
//
//    //父类对象不能赋值给子类对象
//    //sobj = (Student)pobj;
//
//    //父类的指针或者引用可以通过强制类型转换赋值给子类的指针或者引用,
//    //但是父类的指针或者引用指向子类对象才是安全的
//    Student* ps = (Student*)pp;
//
//    int i = 1;
//    double d = i;
//    const double& rd = i;//临时变量具有常性
//    sobj.test1(10);
//    sobj.Person::test1();//通过指定类域的方式才能访问父类中的同名成员函数
//    return 0;
//}



//实现一个不能被继承的类
//方法一:将父类的构造函数私有化——C++98的方法
// (父类的private子类不能访问,因此如果继承的话子类中父类的部分无法被初始化)
//方法二:class+类名后加上final,这样的 最终类不能被继承,更加直观

//继承和友元
// 父类的友元关系不能被继承给子类
// 解决方法——再在子类里加一个友元声明
// 
// 
// 继承和静态成员
// static成员可以被继承,但无论派生出多少个子类,都只有一个static成员实例
// 公有的情况下,父子类指定类域都可以访问私有成员
// 
// 
// 多继承和菱形继承问题
//    单继承:只有一个直接父类
//    多继承:有两个及其以上的父类,例如class assistant : public teacher, public student
// 菱形继承问题:
//        还是上面的例子class assistant : public teacher, public student
//        含有class teacher : public person; class student : public person
//        数据冗余和二义性的问题:
//        数据冗余:存在歧义。解决方法:指定类域
// 虚继承
//        加上关键字virtual:
//        class teacher:virtual public person
//        class student:virtual public person
//        class assistant:public teacher, public student;
//        解决了数据冗余和二义性的问题,student和teacher就共用了一份
//        哪个类产生了数据冗余和二义性,继承时就用虚继承
//



class base1
{
public:
    int _a;
};

class base2
{
public:
    int _b;
};

class derive : public base1, public base2
{
public:
    int _c = 3;
    int _d = 4;
};

int main()
{
    derive d;
    base1* p1 = &d;
    base2* p2 = &d;
    derive* p3 = &d;
    //这里运行的结果是p1 == p3 != p2(先声明的在前面,后声明的在后面)
    return 0;
}


//继承和组合
// ·继承是一种is-a关系,也就是说每个子类对象都是一个父类对象
// ·组合是一种has-a关系,假设B组合了A,每个B对象中都有一个A对象
// ·继承是一种白盒复用,父类的内部细节对子类可见,但一定程度上破坏了封装性
// ·组合是一种黑箱复用对象的内部细节不可见,要求被组合的对象有定义良好的关系,耦合度更低
// ·优先使用组合
#include <list>
class stack
{
public:
    list<int> _lt;//每个Stack中都有一个list对象
};
//黑盒测试:不了解底层实现,从功能角度
//白盒测试:了解的底层实现,从代码运行逻辑角度测试
//

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值