复习Complex类的实现过程
构建复数Complex类的思考路程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
#ifndef __COMPLEX__
#define __COMPLEX__
#include <iostream> //实际上include不一定要写在前面,只要在函数外面就行
using std::ostream;
class complex
{
public:
complex (double r = 0, double i = 0) : re(r), im(i){}
complex& operator += (const complex&);
double real() const {return re;}
double imag() const {return im;}
private:
double re, im;
friend complex& __doapl(complex*, const complex);
};
#endif
//do assignment-plus,函数中想直接取得re和im,所以声明成友元函数
complex& __doapl(complex* ths, const complex r){
ths->re += r.re;
ths->im += r.im;
return *ths;
}
inline complex& complex::operator += (const complex& r){
return __doapl(this, r);
}
//非成员函数
//把+不设计为成员函数是因为不只是复数加复数,还可以是实数加复数
inline complex operator + (const complex& x, const complex& y){
return complex(x.real() + y.real(), x.imag() + y.imag());
}
inline complex operator + (const complex& x, double y){
return complex(x.real() + y, x.imag());
}
//操作符重载只能用在左边的变量上
inline complex operator + (double x, const complex& y){
return complex(x + y.real(), y.imag());
}
//由于希望能够连用,如cout << c1 << endl; 所以有返回值
ostream& operator << (ostream& os, const complex& x){
return os << '(' << x.real() << x.imag() << ')';
}
|
知识点
- 防卫式声明
- 构造函数初值列
- 类成员函数后面加上const关键字后,这个函数就能够被const对象和非const对象调用
三大函数:拷贝构造,拷贝复制,析构
拷贝构造,拷贝复制,析构函数被称为三大函数(Big Three)。
1
2
3
4
5
6
7
8
9
10
11
|
class String
{
public:
String(const char* cstr = 0); // 基础构造函数
String(const String& str); // 1.拷贝构造函数
String& operator=(const String& str); // 2.拷贝赋值 重载=
~String(); // 3.析构函数
char* get_c_str() const { return m_data; } // 不改data,const
private:
char* m_data;
};
|
- class with pointer member 必须有 copy ctor 和 copy op=,否则会导致下面1. 指向同一个地方,改a,b就跟着改了 。2. 原本b的指向的地方没有清掉。
![image-20220510195526218 image-20220510195526218](/svg/loading/small.min.svg)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// 拷贝构造函数
inline
String::String(const String& str) {
auto len = strlen(str.m_data); // 直接取另一个的data,因为兄弟之间互为friend
m_data = new char[len + 1];
memset(m_data, 0, len + 1);
strcpy(m_data, str.m_data);
}
// 拷贝赋值函数
inline String&
String::operator=(const String& str) {
if (this == &str) { // 0.自我赋值(self assignment)检测,否则直接delete,没有数据了
return *this;
}
delete[] m_data; // 1.清空被赋值的内容
m_data = new char[strlen(str.m_data) + 1]; // 2.申请新的内存
strcpy(m_data, str.m_data); // 3.拷贝
return *this;
}
|
扩展补充:类模板,函数模板,及其他
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Account {
public:
static double m_rate; // 只是声明
static void set_rate(const double& x) { m_rate = x; }
};
double Account::m_rate = 8.0; // 定义:使其获得内存,在类外写
int main() {
// 调用static函数的两个方式:
Account::set_rate(5.0); // 1.通过class name 调用
Account a;
a.set_rate(7.0); // 2.通过object调用
}
|
使用static,并把构造函数写到private中,实现单例模式。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class A {
public:
static A& getInstance() {
static A a; // 放在getInstance函数中,而不是在private的好处:只有当有人调用getInstance的时候,才会分配空间,但是离开函数以后,依然存在。
return a;
}
setup() {...}
private:
A();
A(const A& rhs);
};
A::getInstance().setup();
|
- 但是该成员函数处理的哪个对象,靠this pointer来确定
- 加了static的成员变量:和对象就脱离了;所有对象的该数据都一样,就弄成静态的;
- 加了static的成员函数:静态函数没有this pointer,所以不可以处理对象中的非静态的成员变量;只能处理静态成员变量。
![img img](/svg/loading/small.min.svg)
组合与继承
![image-20220512165130258 image-20220512165130258](/svg/loading/small.min.svg)
queue所有的功能在deque中已经完成,所以调用deque来完成。
这是adapter设计模式(adapter有两种实现方法,复合和继承),用于填补“现有的程序”和“所需的程序”之间差异的设计模式。
![image-20220530191058641 image-20220530191058641](/svg/loading/small.min.svg)
![image-20220602152516398 image-20220602152516398](/svg/loading/small.min.svg)
虽然实现的时候是用指针,但是也叫composition by reference。
和Composition相比,Delegation有一个指针指向实现了所有功能的类(pointer to implementation),外界只看到Handle,左边的Handle不变,只需要改右边,所以被称为编译防火墙。
C++有三种继承(public、private、protected,但是最常用的就是public,java里的继承都是public继承),继承就表明类之间是is-a关系。
虚函数
考虑到继承的时候,需要搭配父类的虚函数。
虚函数:让子类能够重写(override)。其中一类是纯虚函数(pure virtual),父类不实现,子类一定要实现。
![image-20220602154503488 image-20220602154503488](/svg/loading/small.min.svg)
设计模式 Template Method:父类中的一个重要部分延缓到子类中去实现(如下Serialize() ),子类可能要一年或两年后才把它的具体内容写出来。这里Template不是c++中的模板,只是套用概念。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
#include<iostream>
//------ FrameWork
class CDocument
{
public:
void OnFileOpen();
//这里 Serialize() 为 纯虚函数 或 空函数 都可以
virtual void Serialize() = 0;
//virtual void Serialize(){ }
};
void CDocument::OnFileOpen()
{
//如下 每个 std::cout 表示一个 实际动作
std::cout << "dialog..." << std::endl;
std::cout << "check file status..." << std::endl;
std::cout << "Open file..." << std::endl;
//(3) this->Serialize()
// 父类 non-virtual func 中 用 pointer this 调
// 父类 pure virtual func =>
// this -> 运行时 obj/myDoc 的 vptr -> vtbl
// -> 子类中 override 的 vf
Serialize();
std::cout << "close file..." << std::endl;
std::cout << "update all views..." << std::endl;
}
//------ App
class CMyDoc : public CDocument
{
public:
virtual void Serialize()
{
//(4) 子类 override 父类 pure vf
// reason: 只有 application 本身 才知道 如何读取自己的文件
std::cout << "CMyDoc::Serialize()..." << std::endl;
}
};
int main()
{
//(1) create subclass obj
CMyDoc myDoc;
//(2) 通过 子类 obj 调 继承自 父类 的 non-virtual func
// =>
// 1) 静态绑定
// 2) &obj_subclas 作 arg 传给 父类 该 func 的 para this:
// CDocument::OnFileOpen(&myDoc)
// => 形实结合 CDocument* this = &myDoc
// => this/CDocument* downcast 为 &myDoc/CMyDoc*
myDoc.OnFileOpen();
}
|
参考资料