目录
1.访问限定符的变化
简单来说就是取小的public > protected > private,protected与private不同的就是在子类里前者可以被访问,后者不行;对于struct来说默认是public,class默认是private(不写public的时候);派生类中的成员函数可以访问基类的公有和保护成员
2.子类的4个默认成员
cpp
#include <iostream>
#include <string>
using namespace std;
class person
{
public:
person(double height, double weight)
:_height(height)
, _weight(weight)
{
cout << "person()" << endl;
}
person(const person& p)
:_height(p._height)
,_weight(p._weight)
{
cout << "person(const person& p)" << endl;
}
person& operator=(const person& p)
{
if (this != &p)
{
_height = p._height;
_weight = p._weight;
}
cout << "bool operator=(const person& p)" << endl;
return *this;
}
~person()
{
cout << "~person()" << endl;
}
void print()
{
cout << _height << endl;
cout << _weight << endl;
}
protected:
double _height;
double _weight;
};
class student : public person
{
public:
student(int id, double height, double weight)
:person(height, weight)
, _id(id)
{
cout << "student(int id, double height, double weight)" << endl;
}
student(const student& stu)
:person(stu) //会切片,也就是赋值兼容
, _id(stu._id)
{
cout << "student(const student& stu)" << endl;
}
student& operator=(const student& stu)
{
if (this != &stu)
{
person::operator=(stu);
_id = stu._id; //老问题,私有的为什么可以访问
}
cout << "student& operator=(const student& stu)" << endl;
}
~student()
{
cout << "~student()" << endl;
}
void print()
{
cout << person::_height << endl;
cout << _height << endl;
}
private:
int _id;
};
继承就是为了复用??**(gpt)**创建一个子类对象,就要初始化,父类有,直接取调用父类的函数;
对于析构函数来说,不需要调用父类,因为一个子类创建的时候,先初始化的父类,在初始化子类的部分,有点栈的味道,那么肯定是要先析构子类的那部分,后创建先析构
比如说这么写就有两个,但是为什么不报错呢?因为没有开辟堆上的空间,根本不需要析构,有成员变量是需要申请动态内存的,就会报错**(GPT)**
父类的析构是public,为什么这里报错,其实是编译器对析构函数重命名了,每一个类的析构函数都变成了destructor**(GPT)(说不会)**
老问题:_id(stu._id);_id = stu._id;虽然说_id是私有的但是它是student类的成员函数
3.切片,赋值兼容
对象的切片
person(stu)和person::operator=(stu);都是;这是为了初始化,肯定是子类给父类,因为父类给子类有部分初始化不了
指针的切片
4.作用域
1.父类和子类有独立的作用域
2.父类和子类有同名的成员(变量,函数(函数名相同即可)),那么子类会优先访问自己的,也就是屏蔽对父类的直接访问,这种情况叫做隐藏或者重定义
3.建议不要出现隐藏(大概率和多态有关**(GPT)**)
那么这时候需要访问父类,需要加上作用域cout << person::_height << endl;就像这样,前提是你需要有权限,即:子类的继承方式和父类的_height成员权限取小是>= protected
5.友元函数与继承
父类的友元关系不可以继承给子类
看,我只给父类加了友元,下面的print1(stu)怎么没报错?
实际上是切片了,监视里p和stu的地址是一样的
6.静态成员的继承
对于父类的静态成员,所有子类共和父类公用,只有一个static成员实例
7.菱形继承
1.多继承是子类继承了两个及以上直接父类,如果这两个父类有相同的父类,那么就是菱形继承
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class A
{
public:
int _a = 1;
};
class B : public A
{
public:
int _b = 2;
};
class C : public A
{
public:
int _c = 3;
};
class D : public B, public C
{
public:
int _d = 4;
};
int main()
{
D d;
return 0;
}
1.内存中存储的顺序和继承顺序一样,父类的放在子类的前面
2.这里的A叫做虚基类
如果要在类外修改,首先要是公有成员,然后通过域作用限定符来确定是哪一个_a -> d.B::_a;
菱形继承的问题:
数据冗余,二义性
8.菱形虚拟继承
1.添加virtual的位置是拥有共同父类的子类的位置
2.B和C第一个位置放的是虚基表的指针
3.虚基类会放到最下面,变成公共的
4.00 10 7b dc是一个虚基表指针
0x0055F9D8+14就是_a的位置,存放的是成员变量的偏移量
&b:对于B实例化出来的对象也是一样的对象模型
9.部分名词的解释
高内聚:一个模块(类、函数等)内部各个成员之间相关性的程度,具有高内聚的模块内的成员彼此之间紧密相关,具有更好的复用性。好处:减少重复,灵活性;坏处:过度耦合, 增加了修改和测试的难度功能冗余可能会使得代码的维护和更新变得更加困难
模块:模块是指一个相对独立的、可重用的、功能性的软件单元。模块可以是一个类、一个函数、一个源代码文件,甚至是一个完整的动态库(也称为共享库)。模块化的设计可以提高软件的可维护性和可扩展性,并且有助于降低系统复杂度。
动态库:一种包含可重用代码的文件,它可以在运行时被加载到内存中,并且可以被多个程序共享使用。动态库通常包含一组相关的模块,这些模块提供了特定的功能或服务。因此,动态库可以被视为一种特殊类型的模块,它提供了更大的复用性和灵活性。
耦合度:指的是模块之间的依赖关系程度。低耦合度意味着模块之间的依赖关系较弱,修改一个模块不会对其他模块产生较大的影响;低耦合度是软件设计中追求的目标之一,可以提高软件的灵活性和可维护性。低耦合度的好处:独立性 使得类之间的依赖关系降低,提高了系统的模块化程度,使得各个模块可以相对独立地开发、测试和维护。坏处:设计复杂性 引入更多的抽象和接口,增加了系统的设计复杂性性能损失
总结:
继承体系中子类 >= 父类,子类的所有成员变量父类中都有,就是有没有访问权限的问题
如果父类有默认构造,那么在派生类构造函数初始化列表的位置,则不必须显式调用基类的构造函数
隐藏不需要参数不同
GPT的回答
但是好像是破坏封装
没次数了,要重新问,它没理解我的意思