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 =
Person sobj;
   
// 2.基类对象不能赋值给派生类对象
   sobj = pobj;
   
   // 3.基类的指针可以通过强制类型转换赋值给派生类的指针
   pp = &sobj
   Student* ps1 = (Student*)pp; // 这种情况转换是可以的
   ps1->_No = 10;
   
   pp =
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 << endl;
}
   
// 赋值重载函数
Person& operator=(const Person& p )
{
cout << "
Person operator=(const Person << 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 << endl ;
}

// 自己实现派生类的赋值重载函数
Student& operator = (const Student& s )
{
cout << "
Student& operator= (const Student << 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 // 声明该函数是基类的友元
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 = // B对象指针 -> D对象,把D对象切片给B对象指针
p1->_a; // 指针访问虚基类A的成员_a
B* p2 = // 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 条评论