2024年最全C++—继承,2024年最新后台开发C C++岗

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

Person& ref = s;

//2.父类对象不能赋值给子类
s = p;

//3.父类的指针和引用可以强转赋给子类的指针
Student\* sptr = (Student\*)&p;
Student& rref = (Student&)p;
return 0;

}


总结:


* 常说的**切片**,寓意就是把子类中,父类的那一部分切下来,赋值过去。(这里也叫赋值兼容,仅限于public继承),这里不存在类型的转换,是天然的语法支持行为。
* 父类是不能够赋值给子类的。
* 父类的指针可以通过强转类型赋值给子类,但是很危险,存在访问越界的风险。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/655e58c426524da18aadc57726bfe7cd.png)


## 三、继承中的作用域


1. 在继承体系中,父类和子类都有独立的作用域
2. 子类和父类中有同名成员时,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫做隐藏,也叫做重定义。(在子类成员函数中,可以显式指出要访问的函数,使用`基类::基类成员`)



class Person
{
protected:
string _name = “小李子”; // 姓名
int _num = 111; // 身份证号
};
class Student : public Person
{
public:
void Print()
{
cout << " 姓名:" << _name << endl;
cout << " 身份证号:" << Person::_num << endl;//指定域
cout << " 学号:" << _num << endl;
}
protected:
int _num = 999; // 学号
};
int main()
{
Student s1;
s1.Print();
return 0;
}


父子类出现了相同的变量,子类对父类的\_num变量会隐藏起来,想要访问父类的成员,必须指定类域。


3. 如果是成员函数的隐藏,只要函数名字相同,就构成隐藏。



class A
{
public:
void fun()
{
cout << “func()” << endl;
}
};
class B : public A
{
public:
void fun(int i)
{
cout << “func(int i)->” << i << endl;
}
};

int a = 90;
int main()
{
//int a = 900;
//cout << ::a << endl;//局部优先,指定全局域 ::
B b;
b.fun(100);

b.A::fun();//在子类对象中,指定域访问父类成员函数

b.fun();//被隐藏了,调不动
return 0;

}


注意:最好在继承里面不要定义同名的成员!


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


6个默认成员函数,默认的意思就是,我们不写,编译器自动生成的,那么在子类中编译器默认生成的,会干什么呢?



> 
> 我们不写默认生成的子类的构造和析构怎么处理?  
>  a:对于继承下来的成员,调用父类的默认构造和析构函数  
>  b:对于自己的成员,根普通的类处理方式一样(内置类型不做处理,自定义类型调用它对应的默认构造和析构函数)
> 
> 
> 



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:
//不写,调用父类的构造和析构
protected:
int _num; //学号
string _s=“hello”;
};
int main()
{
Student s;
return 0;
}


结果展示:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6a05f1ae4aae46c183949b7994ef6a65.png)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9b70136668154683bf543f285d51fe1d.png)



> 
> 我们不写默认生成的子类的拷贝构造和operator= 怎么处理?  
>  a:对于继承下来的成员,调用父类的拷贝构造和operator=  
>  b:对于自己的成员,跟普通类一样处理
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/b12f410bf3d845efa7e89a9a21a83917.png)


总结:原则上,继承下来的成员调用父类的处理,自己的成员按照普通类的处理即可。



> 
> 什么情况下必须自己写?
> 
> 
> 


1. 父类没有默认构造,需要我们自己显示写构造。
2. 如果子类有资源需要释放,就需要自己显示写析构。(比如有new的资源)
3. 如果子类存在浅拷贝问题,就需要自己实现拷贝构造和赋值解决浅拷贝问题。



> 
> 如果我们要自己写,如何自己写?
> 
> 
> 


1. 父类成员调用父类的对应构造、拷贝构造、operator=和析构处理
2. 自己成员按普通类处理。



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=“zhangsan”,int num=0)
:Person(name)//不能这样写 _name(name)
,_num(num)
{}

Student(const Student& s)
	:Person(s)//这里s会自动切片传给父类
	,\_num(s._num)
{}

Student& operator=(const Student& s)
{
	if (this != &s)
	{
		Person::operator=(s);//与父类的构成了隐藏,必须指定类域
		_num = s._num;
	}
	return \*this;
}
~Student()
{}

protected:
int _num; //学号
};
int main()
{
Student s;
Student s1(s);

s1 = s;

return 0;

}


有一个细节知识点:  
 父子析构函数名字会被统一处理成为"`destructor( )`",那么父子的析构函数就会构成隐藏关系,当子类调用析构函数时,必须指定作用域。



~Student()
{
Person::~Person();//指定域
}


![在这里插入图片描述](https://img-blog.csdnimg.cn/259da3c3d8e24e71b318bc59a987f723.png)  
  子类析构函数结束时,出了作用域会自动调用父类的析构函数,所以我们自己实现析构函数时,不需要显示调用父类的析构函数,这样才能保证先析构子类成员,后析构父类成员。(创建s对象时,里面继承的有父类的那一部分,会被先构造父类成员,后构造子类成员,析构时先析构子类成员,后析构父类成员)


## 五、继承与友元


**友元关系不能继承**,也就是说基类友元不能访问子类私有和保护成员



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;
}


## 六、继承与静态成员


基类定义了一个static静态成员,则整个继承体系里面只有这么一个成员,无论派生多少个子类,都只有一个static成员。



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; // 研究科目
};
int main()
{
Person p; //调用一次构造函数
Student s; //调用一次父类的构造函数
Graduate g; //调用一次父类的构造函数

cout << Person::_count << endl;   // 3
cout << Student::_count << endl;  // 3
cout << Graduate::_count << endl; // 3

cout << &Person::_count << endl;   // 地址都相同
cout << &Student::_count << endl;  // 地址都相同
cout << &Graduate::_count << endl; // 地址都相同
return 0;

}


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


#### 单继承


一个子类只有一个直接父类时称为单继承  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/a930c8fccb494e36b36d0a2d3074668d.png)


#### 多继承


一个子类有两个或者两个以上的直接父类时称为多继承  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/571c5db6174842418ea9b4c78294419f.png)


#### 菱形继承


菱形继承是多继承的一种特殊情况  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/619a9e0e9dd4433094da1c1dcbb9c2c3.png)


菱形继承的**问题**:**数据冗余和二义性**  
 从下面的对象成员来看,在Assistant的对象中就有两份Person成员,访问\_name时就不知道访问谁的,就会导致问题。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/bf6f9fb6cb0f4f94a3b425890e172459.png)



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; // 主修课程
};
void Test()
{
// 这样会有二义性无法明确知道访问的是哪一个
Assistant a;
a._name = “peter”;
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a.Student::_name = “xxx”;
a.Teacher::_name = “yyy”;
}


#### 虚拟继承




![img](https://img-blog.csdnimg.cn/img_convert/338c4c01cc5222101d05227d39b087e3.png)
![img](https://img-blog.csdnimg.cn/img_convert/daf9d7fc7bcf36178e131bee6ce91078.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

	Assistant a;
	a._name = "peter";
	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决 
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
}

虚拟继承

[外链图片转存中…(img-sY4JnaZ9-1715834156337)]
[外链图片转存中…(img-gjH7hULm-1715834156337)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值