目录

C++面向对象高级编程(上)

复习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

 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

组合与继承

image-20220512165130258

queue所有的功能在deque中已经完成,所以调用deque来完成。

这是adapter设计模式(adapter有两种实现方法,复合和继承),用于填补“现有的程序”和“所需的程序”之间差异的设计模式。

image-20220530191058641

image-20220602152516398

虽然实现的时候是用指针,但是也叫composition by reference。

和Composition相比,Delegation有一个指针指向实现了所有功能的类(pointer to implementation),外界只看到Handle,左边的Handle不变,只需要改右边,所以被称为编译防火墙。

C++有三种继承(public、private、protected,但是最常用的就是public,java里的继承都是public继承),继承就表明类之间是is-a关系。

虚函数

考虑到继承的时候,需要搭配父类的虚函数。

虚函数:让子类能够重写(override)。其中一类是纯虚函数(pure virtual),父类不实现,子类一定要实现。

image-20220602154503488

设计模式 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();
}

参考资料