0618
C++工程师
- 第5章 高频考点与真题精讲
-
- 5.1 指针 & 5.2 函数
- 5.3 面向对象(和5.4、5.5共三次直播课)
- 5.6 内存管理①(结合计算机操作系统笔记)
- 5.7 内存管理②(结合计算机操作系统笔记)
- 5.7 名称空间、模板
- 5.8 STL(标准模板库)
- 5.9 C++新特性
第5章 高频考点与真题精讲
5.1 指针 & 5.2 函数
5.3 面向对象(和5.4、5.5共三次直播课)
5.3.1 - 5.3.11
5.3.12-14 友元
友元分为三种:
- 友元全局函数
- 友元成员函数
- 友元类
友元的目的就是让一个 函数或者类 能够访问另一个类中私有成员,关键字是 friend
。重载<<
运算符时,会用到友元。
友元全局函数
一个全局函数想访问一个类中的私有成员变量,那么就把这个全局函数设置为这个类的友元(friend),就可以访问了。
友元类
一个好朋友类想访问Building
类中的私有成员变量,那么就把好朋友类设置为Building
类的友元(friend),就可以访问了。
友元成员函数
好朋友类的成员函数visit()
访问另一类的对象的私有成员变量:
成员函数visit()
在类外实现:
Building类中把GoodFriend类的成员函数visit()
设置为友元,就可以访问自己的m_BedRoom
属性;
但GoodFriend类的成员函数visit1()
没被设置为友元,所以还是无法访问到自己的m_BedRoom
属性。
友元的注意事项
友元关系:
1.不能被继承; 2.是单向的; 3.不具有传递性。
5.3.15-23 运算符重载 -->静态多态
运算符 + = == != ++ () << 重载
C++笔记10:运算符重载
概念:
对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
例如:加号运算符重载的作用就是实现两个自定义数据类型(类)相加的运算。
实现的方式有两种:
- 利用成员函数重载
- 利用全局函数重载
C++笔记10:运算符重载中实现了:
- 加号运算符(
+
)重载 - 左移运算符(
<<
)重载
改进 - 递增运算符(
++
)重载(前置++ 和 后置++) - 赋值运算符(
=
)重载
如果类的成员变量在堆区,做赋值操作时就会出现深拷贝与浅拷贝问题。 - 关系运算符重载(
==
和!=
) - 函数调用运算符
()
重载—仿函数(在STL
中用的比较多)
运算符 | 成员函数实现 | 全局函数实现 | 备注 |
---|---|---|---|
加号运算符(+ )重载 |
Students operator+(const Students& stu){} |
friend Students operator+(const Students& stu1, const Students& stu2){} |
|
左移运算符(<< )重载 |
friend ostream& operator<<(ostream& cout, const Students& stu){} //可以实现连续cout输出 |
||
递增运算符(++ )重载 |
前置++:Students& operator++(){} 后置++: Students operator++(int){} //这个int是为了和前置++形成重载,以通过编译,int本身没啥用 |
前置++:friend Students& operator++(Students& stu){} 后置++: friend Students operator++(Students& stu,int){} //这个int是为了和前置++形成重载,以通过编译,int本身没啥用 |
|
关系运算符(== 和 != )重载 |
bool operator==(Students& stu) {} |
friend bool operator==(const Students& stu1, const Students& stu2){} |
|
赋值运算符(= )重载 |
void operator=(const Person& p){} 升级版: Person& operator=(const Person& p){} //可以实现连续赋值 |
||
函数调用运算符() 重载 |
也叫仿函数 |
(以上是笔记中复制来的内容)
☆☆☆指针运算符重载(* 和 ->)(写个智能指针类)
(视频课从01:32:10开始)
指针的操作就是解引用*
和箭头->
,所以就是重载这两个运算符。
示例:
#include<iostream>
using namespace std;
class Person{
public:
Person(int age){
cout << "Person的构造函数" << endl;
m_Age = age;
}
//输出成员信息:
void showAge(){
cout << "m_Age = " << this->m_Age << endl;
}
~Person(){
cout << "Person的析构函数" << endl;
}
private:
int m_Age;
};
//智能指针类:
class SmartPointer{
public:
//重载箭头运算符->
Person* operator->(){
return this->m_Person;
}
//重载解引用运算符*
Person& operator*(){
return *m_Person;
}
SmartPointer(Person* p){
cout << "SmartPointer的构造函数" << endl;
m_Person = p;
}
~SmartPointer(){
cout << "SmartPointer析构函数" << endl;
if(this->m_Person != nullptr){
delete this->m_Person;
this->m_Person == nullptr;
}
}
//private:
Person* m_Person;//内部维护的Person的指针
};
int main(){
Person* p = new Person(30);
p->showAge();
(*p).showAge();
//正常应该执行下面这行,如果忘了就会造成内存泄漏,所以就用下面的智能指针类
//delete p;
cout << "=================================" << endl;
//智能指针类就是把p替换成了sp,并且在其析构函数中将new的内容delete掉,这样就解决了可能造成的内存泄漏问题
SmartPointer sp(p);
sp->showAge();//相当于sp.operator->()->showAge(); 其中sp.operator->()就相当于p
sp.operator->()->showAge();
//编译器会把sp.operator->()->showAge()优化成sp->showAge();
(*sp).showAge();//相当于sp.operator*().showAge();其中sp.operator*()相当于*p
sp.operator*().showAge();
//编译器会把sp.operator*().showAge()优化成(*sp).showAge();
return 0;
}
编译运行:
Person的构造函数
m_Age = 30
m_Age = 30
=================================
SmartPointer的构造函数
m_Age = 30
m_Age = 30
m_Age = 30
m_Age = 30
SmartPointer析构函数
Person的析构函数
运算符重载的应用:封装字符串类
视频课:点这里
5.3.24 类的自动类型转换和强制类型转换(关键字explicit
)
被explicit
修饰的构造函数不能用于自动类型转换(隐式类型转换)。
示例:
#include <iostream>
using namespace std;
class MyString {
public:
// 被explicit修饰的构造函数不能用于自动类型转换(隐式类型转换)
explicit MyString(int len) {
cout << "MyString有参构造MyString(int len)..." << endl;
}
//explicit
MyString(const char* str) {
cout << "MyString有参构造MyString(const char* str)..." << endl;
}
// 转换函数:转换成double
operator double() {
// 业务逻辑
return 20.1;
}
};
// 类的自动类型转换和强制类型转换
/*
如果我们想让类对象转换成基本类型的数据,我们需要在类中添加转换函数
1.转换函数必须是类方法
2.转换函数不能指定返回类型
3.转换函数不能有参数
*/
int main() {
// 基本内置数据类型的自动类型转换和强制类型转换
long count = 8;
double time = 10;
int size = 3.14;
cout << count << endl; //8
cout << time << endl; //10
cout << size << endl; //3
double num = 20.3;
cout << num << endl; //20.3
// 强制转换成int类型的数据
int num1 = (int)num;
int num2 = int(num);
cout << num1 << endl; //20
cout << num2 << endl; //20
MyString str = "hello"; //
// MyString str = MyString("hello");
// MyString str1 = 10; //不能通过隐式类型转换创建对象了
MyString str1 = MyString(10);
// 类的强制类型转换
double d = str1;
cout << d << endl; //20.1
double d1 = double(str);
double d2 = (double)str;
cout << d1 << endl; //20.1
cout << d2 << endl; //20.1
return 0;
}
结果:
8
10
3
20.3
20
20
MyString有参构造MyString(const char* str)...
MyString有参构造MyString(int len)...
20.1
20.1
20.1
5.3.25-34 继承
继承的概念、好处、弊端
继承是面向对象的一大特征。
继承好处:
- 提高代码的复用性;
- 提高代码的维护性(方便维护);
- 让类与类之间产生了关系,是(动态)多态的前提。
继承的弊端:
- 类的耦合性增加了
开发的原则是:高内聚,低耦合
内聚:自己完成一件事的能力;
耦合:类和类之间的关系。
继承的基本语法
继承的语法:
class 子类:继承方式 父类{
...
};
例如:
class dogs: public Animals {
...
};
继承方式分为public,protected,private
,即公共继承,保护继承,私有继承。
注意:
如果不写继承方式,默认是私有继承。
protected访问权限
三种权限:
public:
公共的访问权限,被修饰的成员在类内和类外都能够被访问;protected:
受保护的访问权限,如果没有继承关系,就和private的特点一样;privated:
私有的访问权限,被修饰的成员只能在类内被访问,在类外不能够被访问;
在继承关系中,父类中的protected修饰的成员,子类中可以直接访问,但在类外的其他地方不能访问。
成员变量一般使用privated
私有访问控制,不要使用protected
受保护的访问控制;
成员方法如果想要让子类访问,但是不想让外界访问,就可以使用protected
受保护的访问控制。
(下面的内容来自C++笔记3:C++核心编程中的4.6.2 继承方式)
总结:
1.父类中的私有内容(private
)任何一种继承方式都访问不到,即无法被访问/被继承;
2.公共继承:父类中的各访问权限不变
3.保护继承:父类中的各访问权限都变成protected
保护权限
4.私有继承:父类中的各访问权限都变成private
私有权限
无继承关系:
父类中的三个成员变量 | 父类类内访问 | 类外通过父类对象访问 |
---|---|---|
public 修饰的num1 |
可以访问 | 可以访问 |
protected 修饰的num2 |
可以访问 | 不能访问 |
private 修饰的num3 |
可以访问 | 不能访问 |
子类公共继承父类:class Zi : public Fu{ ... };
父类中的三个成员变量 | 子类中的三个成员变量 | 父类类内访问 | 子类类内访问 | 类外通过父类对象访问 | 类外通过子类对象访问 |
---|---|---|---|---|---|
public 修饰的num1 |
public 修饰的num1 |
可以访问 | 可以访问 | 可以访问 | 可以访问 |
protected 修饰的num2 |
protected 修饰的num2 |
可以访问 | (因为有继承关系,所以) 可以访问 |
不能访问 | 不能访问 |
private 修饰的num3 |
private 修饰的num3 |
可以访问 | 不能访问 | 不能访问 | 不能访问 |
子类保护继承父类:class Zi : protected Fu{ ... };
父类中的三个成员变量 | 子类中的三个成员变量 | 父类类内访问 | 子类类内访问 | 类外通过父类对象访问 | 类外通过子类对象访问 |
---|---|---|---|---|---|
public 修饰的num1 |
protected 修饰的num1 |
可以访问 | 可以访问 | 可以访问 | 不能访问 |
protected 修饰的num2 |
protected 修饰的num2 |
可以访问 | (因为有继承关系,所以) 可以访问 |
不能访问 | 不能访问 |
private 修饰的num3 |
private 修饰的num3 |
可以访问 | 不能访问 | 不能访问 | 不能访问 |
子类私有继承父类:class Zi : private Fu{ ... };
父类中的三个成员变量 | 子类中的三个成员变量 | 父类类内访问 | 子类类内访问 | 类外通过父类对象访问 | 类外通过子类对象访问 |
---|---|---|---|---|---|
public 修饰的num1 |
private 修饰的num1 |
可以访问 | 可以访问 | 可以访问 | 不能访问 |
protected 修饰的num2 |
private 修饰的num2 |
可以访问 | (因为有继承关系,所以) 可以访问 |
不能访问 | 不能访问 |
private 修饰的num3 |
private 修饰的num3 |
可以访问 | 不能访问 | 不能访问 | 不能访问 |
示例:
#include<iostream>
using namespace std;
class Fu{
public:
int num1;
protected:
int num2;
private:
int num3;
public:
void func(){
num1 = 10;
num2 = 20;
num3 =