0
点赞
收藏
分享

微信扫一扫

ESP32网络开发实例-UDP数据发送与接收

一、继承的概念及定义

1、继承的概念

// 基类
class Person
{
protected:
    void print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
    string _name = "张三"; // 姓名
    int _age = 18;  // 年龄
};

// 派生类
class Student : public Person
{
protected:
    int _stuid; // 学号
};

// 派生类
class Teacher : public Person
{
protected:
    int _jobid; // 工号
};

int main()
{
    Person p;
	p.Print();

    Student s;
    s.print();

    Teacher t;
    t.print();

    return 0;
}


2、继承定义

(1)定义格式

 


(2)继承关系和访问限定符


(3)继承基类成员访问方式的变化

下面这个表格不需要记忆,通过理解加深印象即可。

(红色框住的是常用的)

实例演示三种继承关系下基类成员的各类型成员访问关系的变化:

// 基类
class Person
{
public :
    void Print ()
    {
        cout << _name << endl;
    }
protected :
    string _name ; // 姓名
private :
    int _age ; // 年龄
};

// 派生类
//class Student : protected Person
//class Student : private Person
class Student : public Person
{
protected :
    int _stunum ; // 学号
};
⚪【补充】 

二、基类和派生类对象赋值转换

// 基类
class Person
{
protected :
    string _name; // 姓名
    string _sex;  // 性别
    int _age; // 年龄
};

// 派生类
class Student : public Person
{
public :
    int _id; // 学号
};

void Test ()
{
    Student sobj ;
    // 1.子类对象可以赋值给父类对象/指针/引用
    Person pobj = sobj ;
    Person* pp = &sobj;
    Person& rp = sobj;
    
    // 2.基类对象不能赋值给派生类对象
    sobj = pobj;
    
    // 3.基类的指针可以通过强制类型转换赋值给派生类的指针
    pp = &sobj
    Student* ps1 = (Student*)pp; // 这种情况转换是可以的
    ps1->_No = 10;
    
    pp = &pobj;
    Student* ps2 = (Student*)pp; // 这种情况转换虽然可以,但是会存在越界访问的问题
    ps2->_No = 10;
}

三、继承中的作用域

// Student的_num和Person的_num构成隐藏关系,虽然这样的代码虽然能跑,但是非常容易混淆

// 基类
class Person
{
protected :
    string _name = "李四"; // 姓名
    int _num = 333444;   // 身份证号
};

// 派生类
class Student : public Person
{
public:
    void Print()
    {
        cout << "姓名:" << _name << endl;
        cout << "身份证号:" << Person::_num << endl; // 类名::成员显示访问
        cout << "学号:" << _num << endl; // 打印的是派生类的学号
    }
protected:
    int _num = 111222; // 学号
};

int main()
{
    Student s1;
    s1.Print();

    return 0;
};

class A
{
public:
    void fun()
    {
        cout << "func()" << endl;
    }
};

class B : public A
{
public:
    void fun(int i)
    {
        A::fun(); // 类名::成员 还是可以访问的
        cout << "func(int i)->" << i << endl;
    }
};

int main()
{
    B b;
    b.fun(10);

    return 0;
};


四、派生类的默认成员函数

// 基类
class Person
{
public :
    // 默认构造函数
    Person(const char* name = "Peter")
        : _name(name )
    {
        cout << "Person()" << endl;
    }
    
    // 拷贝构造函数
    Person(const Person& p)
        : _name(p._name)
    {
        cout << "Person(const Person& p)" << endl;
    }
    
    // 赋值重载函数
    Person& operator=(const Person& p )
    {
        cout << "Person operator=(const Person& p)" << endl;
        if (this != &p)
            _name = p ._name;
        
        return *this ;
    }
    
    // 析构函数
    ~Person()
    {
        cout << "~Person()" << endl;
    }
protected :
    string _name ; // 姓名
};

//派生类
class Student : public Person
{
public :
    // 自己实现派生类的构造函数
    // 注意:继承的基类成员是作为一个整体,调用基类的构造函数进行初始化
    Student(const char* name, int num)
        : Person(name) // 显示调用基类的构造函数
        , _num(num)
    {
        cout << "Student()" << endl;
    }

    // 如果我们要自己实现派生类的拷贝构造,如下所示:
	// 但一般用编译器默认生成的即可,如果派生类中存在深拷贝,才需要自己实现
    Student(const Student& s)
        : Person(s) // 必须显示调用基类的拷贝构造(这里会发生切片)
        , _num(s ._num)
    {
        cout << "Student(const Student& s)" << endl ;
    }
 
    // 自己实现派生类的赋值重载函数
    Student& operator = (const Student& s )
    {
        cout << "Student& operator= (const Student& s)" << endl;
        if (this != &s)
        {
            Person::operator =(s); // 必须显示调用基类的赋值重载(这里会发生切片)
            _num = s ._num;
        }
        return *this ;
    } 
 
    ~Student() // 先清理自己的资源
    {
        cout << "~Student()" << endl;
    } // 结束后会自动调用父类的析构函数
protected :
    int _num ; //学号
};

int main()
{
    Student s1 ("Jack", 18);// 调用构造函数
    Student s2 (s1); // 调用拷贝构造函数
    Student s3 ("Rose", 17);
    s1 = s3 ; // 调用重载赋值函数

    return 0;
}

五、继承与友元

class Student;

// 基类
class Person
{
public:
    friend void Display(const Person& p, const Student& s); // 声明该函数是基类的友元
protected:
    string _name; // 姓名
};

// 派生类
class Student : public Person
{
protected:
    int _stuNum; // 学号
};

void Display(const Person& p, const Student& s)
{
    cout << p._name << endl; // 你是基类的友元,访问基类对象是可以的
    cout << s._stuNum << endl; // 友元不继承你不能访问派生类对象,这里会报错,显示不可访问
}

int main()
{
    Person p;
    Student s;
    Display(p, s);

    return 0;
}

六、继承与静态成员

//无论继承多少层,大家用的始终是同一个变量

// 基类
class Person
{
public :
    Person () {++ _count ;}
protected :
    string _name ; // 姓名
public :
    static int _count; // 统计人的个数
};

int Person :: _count = 0;
class Student : public Person
{
protected :
    int _stuNum ; // 学号
};

class Graduate : public Student
{
protected :
    string _seminarCourse ; // 研究科目
};

void TestPerson()
{
    Student s1 ;
    Student s2 ;
    Student s3 ;
    Graduate s4 ;
    cout << " 人数 :" << Person ::_count << endl;
    Student ::_count = 0;
    cout << " 人数 :" << Person ::_count << endl;
}

七、复杂的菱形继承及菱形虚拟继承

 



 

class Person
{
public :
    string _name ; // 姓名
};

class Student : public Person
{
protected :
    int _num ; //学号
};

class Teacher : public Person
{
protected :
    int _id ; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected :
    string _majorCourse ; // 主修课程
};

int main()
{
    // 这样会有二义性无法明确知道访问的是哪一个
    Assistant a ;
    a._name = "Peter";
    // 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
    a.Student::_name = "xxx";
    a.Teacher::_name = "yyy";

    return 0;
}
class Person
{
public :
    string _name ; // 姓名
};

class Student : virtual public Person
{
protected :
    int _num ; //学号
};

class Teacher : virtual public Person
{
protected :
    int _id ; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected :
    string _majorCourse ; // 主修课程
};

int main()
{
    Assistant a ;
    a._name = "Peter"; //这个时候_name其实只有一个

    // 这下面两个显示访问其实访问的都是一个变量
    a.Student::_name = "xxx";
    a.Teacher::_name = "yyy";

    return 0;
}
class A
{
public:
    int _a;
};

// class B : public A
class B : virtual public A
{
public:
    int _b;
};

// class C : public A
class C : virtual public A
{
public:
    int _c;
};

class D : public B, public C
{
public:
    int _d;
};

int main()
{
    D d;
    d.B::_a = 1;
    d.C::_a = 2;
    d._b = 3;
    d._c = 4;
    d._d = 5;

    return 0;
}

为什么 D 中 B 和 C 部分要去找属于自己的 A ?那么看看当下面的赋值发生时,d 是不是要去找出 B/C 成员中的 A 才能赋值过去?
D d;
B b;
B* p1 = &d; // B对象指针 -> D对象,把D对象切片给B对象指针
p1->_a;     // 指针访问虚基类A的成员_a
B* p2 = &b; // B对象指针 -> B对象
p2->_a;     // 指针访问虚基类A的成员_a
(1)为什么需要偏移量? 
(2)为什么不在 0x005EF75C、0x005EF764 直接存偏移量?
(3)virtual 已经能解决菱形继承所带来的问题了,为什么还是不建议使用?

下面是上面的 Person 关系菱形虚拟继承的原理解释:


八、继承的总结和反思

// Car和BMW Car和Benz构成is-a的关系
class Car{
protected:
    string _colour = "白色"; // 颜色
    string _num = "粤DK0896"; // 车牌号
};
   
class BMW : public Car{
public:
    void Drive()
    {
        cout << "好开-操控" << endl;
    }
};
   
class Benz : public Car{
public:
    void Drive()
    {
        cout << "好坐-舒适" << endl;
    }
};
   
// Tire和Car构成has-a的关系
class Tire{
protected:
    string _brand = "Michelin"; // 品牌
    size_t _size = 17;          // 尺寸
};
   
class Car{
protected:
    string _colour = "白色";  // 颜色
    string _num = "粤DK0896"; // 车牌号
    Tire _t; // 轮胎
};
举报

相关推荐

0 条评论