• 笔记:C++面向对象高级编程--侯捷


    Complex--class without pointer member(s)

    防卫式申明

    防止头文件被重复包含

    #ifndef __COMPLEX__
    #define __COMPLEX__
    ...
    #endif
    

    构造函数

    class complex
    {
    	public:
    	complex (double r = 0, double i = 0) : re (r), im (i)  //initialization list
    	{ }
        /*
        complex (double r = 0, double i = 0)    assignments赋值
    	{ 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&);
    };
    

    构造函数优先使用初始化列表,效率更高。使用赋值函数的形式效率更低。

    对于单纯访问成员变量,而不对成员变量进行修改的函数,加上const对变量进行保护。

    函数的入参尽量使用引用传参(pass by reference)

    函数的返回值,如果返回函数内部的局部变量,返回值使用值传递(pass by reference),若返回值为外部变量,在函数执行完毕后,变量的生命周期未结束,使用引用传递作。

    class template 模板类

    template<typename T>
    class complex
    {
        public:
        complex (T r = 0, T i = 0): re (r), im (i){ }       //函数1
        complex& operator += (const complex&);
        T real () const { return re; }                      //函数2
        T imag () const { return im; }                      //函数3
        private:
        T re, im;
        friend complex& __doapl (complex*, const complex&);
    };
    
    {
        complex<double> c1(2.5,1.5);
        complex<int> c2(2,6);
        ...
    }
    

    函数若在class body内定义完成(上述函数1、2、3),便自动成为inline候选人,inline只是对编译器的一种建议,若函数复杂,即使加上了inline,编译器仍然将其编译为正常函数。

    构造函数放在privite---单例模式

    class A {
    public:
        static A& getInstance();
        setup() { ... }
    private:
        A();
        A(const A& rhs);
        ...
    };
    
    A& A::getInstance()
    {
        static A a;
        return a;
    }
    

    friend (友元)

    有元函数自由取得friend的private成员变量。

    inline complex& __doapl (complex* ths, const complex& r)
    {
        ths->re += r.re;
        ths->im += r.im;
        return *ths;
    }
    

    相同 class 的各個 objects 互為 friends (友元)。

    class complex
    {
    public:
        complex (double r = 0, double i = 0): re (r), im (i)
        { }
    	int func(const complex& param)
    	{ return param.re + param.im; }  //随意访问不同objects的private成员变量
        private:
        double re, im;
    };
    

    操作符重载---operator overloading

    成员函数重载(含this指针)

    对于操作符作用于两侧的函数,可以将其设置为成员函数,调用的时候如:

    inline 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);
    }
    
    {
        complex c1(2,1);
        complex c2(5);
        complex c3(6);
        c2 += c1;
        c3 += c2 += c1;
    }
    

    return by reference 语法分析:

    如上述操作符重载operator +=返回值不是return by reference,而是return by value,则在连加时,第一个+=调用完毕后,返回的是临时变量,而不是c3.故第二个+=不会作用在c3上,导致连续的+=失效。

    非成员函数重载(不含this指针)

    二元操作符

    inline double imag(const complex& x)
    {
    	return x.imag ();
    }
    inline double real(const complex& x)
    {
    	return x.real ();
    }
    
    //为了应对用户可能出现的三种用法,这里对应开发了三个函数
    
    inline complex operator + (const complex& x, const complex& y)   //复数+复数
    {
    	return complex (real (x) + real (y), imag (x) + imag (y));
    }
    inline complex operator + (const complex& x, double y)          //复数+实数
    {
    return complex (real (x) + y, imag (x));
    }
    inline complex operator + (double x, const complex& y)         //实数+复数
    {
    return complex (x + real (y), imag (y));
    }
    
    {
        complex c1(2,1);
        complex c2;
        c2 = c1 + c2;
        c2 = c1 + 5;
        c2 = 7 + c1;
    }
    

    上述函数均不能使用return by reference,而是return by value,因为返回值都是local object 。

    一元操作符

    inline complex operator + (const complex& x)   //可以return by reference
    {
    	return x;
    }
    inline complex operator - (const complex& x)   //local object,不能return by reference
    {
    	return complex (-real (x), -imag (x));
    }
    

    非成员函数

    #include <iostream.h>
    ostream& operator << (ostream& os, const complex& x)
    {
    	return os << '(' << real (x) << ','<< imag (x) << ')';
    }
    
    {
        cout << c1;
        cout << c1 << c2;
    }
    

    如果不将operator <<设置为外部函数,而将其设置为complex的成员函数,则在调用的时:

    c1 << cout;   //不符合常规的用法
    

    String--class with pointer member(s)

    Big Three

    由于对象带有指针变量,必须重写拷贝构造函数、拷贝赋值函数、析构函数。

    class String
    {
    public:
        String(const char* cstr = 0);
        String(const String& str);
        String& operator=(const String& str);
        ~String();
    	char* get_c_str() const { return m_data; }
    private:
    	char* m_data;
    }
    

    构造函数和析构函数

    
    inline String::String(const char* cstr = 0)
    {
        if (cstr) {
        	m_data = new char[strlen(cstr)+1];
        	strcpy(m_data, cstr);
        }
        else { // 未指定初值
        	m_data = new char[1];
        	*m_data = '';
    	}
    }
    inline String::~String()
    {
    	delete[] m_data;
    }
    
    //示例
    {
        String s1(),
        String s2("hello");
        String* p = new String("hello");
        delete p;
    }
    

    拷贝构造函数

    inline String::String(const String& str)
    {
        m_data = new char[ strlen(str.m_data) + 1 ];
        strcpy(m_data, str.m_data);
    }
    
    //示例
    {
        String s1("hello ");
        String s2(s1);
        // String s2 = s1;     仍然调用的是拷贝构造函数
    }
    

    必须为pass by reference,如果pass by value,则会导致在入参时不断重复调用拷贝构造函数。

    拷贝赋值函数

    inline String& String::operator=(const String& str)
    {
        if (this == &str)                               //检测自我赋值                  
        	return *this;
        	
        delete[] m_data;                                //释放原有内存
        m_data = new char[ strlen(str.m_data) + 1 ];    //申请新的内存
        strcpy(m_data, str.m_data);                     //拷贝内容
        return *this;
    }
    
    //示例
    {
        String s1("hello ");
        String s2(s1);
        s2 = s1;
    }
    

    上述检测自我赋值if (this == &str)的必要性:

    • 提高算法的执行效率
    • 若无检测自我赋值,进入函数后,第一时间释放了原有资源,后续申请内存和拷贝内容都会出错,因为原有的空间已经被释放掉了

    output 函数

    #include <iostream.h>                   
    ostream& operator<<(ostream& os, const String& str)     //外部函数
    {
        os << str.get_c_str();
        return os
    }
    

    Stack和Heap

    Stack是存在于某作用域 (scope) 的一块内存空间,例如当你调用函数,函数本身会形成一个stack来放置它所接收的参数以及返回地址。在函数本体内申明的任何变量,其所在的内存块都取自上述Stack。

    Heap,也称为systerm heap,是指由操作系统提供的一块global内存空间,程序可以动态分配(dynamic allocated) ,从中获取若干区块 (blocks)

    class Complex { ... };
    ...
    
    Complex c3(1,2);
    {
    	Complex c1(1,2);             //local object
    	static Complex c2(1,2);      //static local object
    	Complex* p = new Complex;
    	...
    	delete p
    }
    

    c1 是 stack object,其生命在作用域 (scope) 结束之后结束。这种作用域內的 object,又稱為 auto object,因为它会被自动清理。

    c2 是 static object,其生命在作用域 (scope)结束之后仍然存在,直到整个程序结束。

    c3 是 global object,其生命在整个程序结束之后才结束。你也可以把它视为一种 static object,其作用域是整个程序。

    P 所指的是 heap object,其生命在它被 deleted 之后结束。

    内存泄漏(memory leak)

    class Complex { … };
    ...
    
    {
    	Complex* p = new Complex;
    }
    

    以上程序会出现内存泄漏 (memory leak),因为当作用域结束后,p所指的heap object 仍然存在,但指針 p 的生命却结束了,作用域之外再也看不到p,也就没有机会delete p,p所指向的内存空间在程序运行期间无法被再次使用。

    new

    Complex *pc;
    
    void* mem = operator new( sizeof(Complex) );   //分配內存
    pc = static_cast<Complex*>(mem);               //类型转换
    pc->Complex::Complex(1,2);                     //构造函數
    

    new的过程分为以上三个步骤,首先,调用operator new申请内存空间,operator new内部最终会调用到malloc(),然后进行类型转换,将其转化为对应的数据类型,最后调用构造函数。

    delete

    String::~String(ps);         // 析构函數
    operator delete(ps);         // 释放内存
    

    delete的过程分为两步,首先调用析构函数,然后调用operator delete释放内存,operator delete最终会调用到free(ps)。

    delete与delete[]

    String* p = new String[3];
    ...
    delete[] p;        //唤起3次析构函数
    
    String* p = new String[3];
    ...
    delete p;          //唤起1次析构函数
    

    上述第二种情况会造成内存泄漏,不是p所指向的内存空间泄漏,而是数组元素所指向的内存空间泄漏。

    Static

    static成员变量不属于任何一个实例化的对象, 不占用对象的内存大小。

    static成员函数入参无this指针,所以无法访问任何一个具体成员的非静态成员变量,只能访问静态成员变量。

    调用static成员函数的方法:

    • 通过object调用
    • 通过class name调用

    Composition (复合).has-a

    template <class T, class Sequence = deque<T> >
    class queue {
        ...
    protected:
        Sequence c;         // 底层容器
    
    public:
        // 以下完全利用 c 的操作函数完成
        bool empty() const { return c.empty(); }
        size_type size() const { return c.size(); }
        reference front() { return c.front(); }
        reference back() { return c.back(); }
        void push(const value_type& x) { c.push_back(x); }
        void pop() { c.pop_front(); }
    };
    

    Composition是一种强耦合关系,在构建queue时,会完成c的构建。

    上述其实使用的是适配器模式,所以queue也被称为适配器容器。

    Delegation (委托). Composition by reference

    class StringRep;
    class String {
    public:
        String();
        String(const char* s);
        String(const String& s);
        String &operator=(const String& s);
        ~String();
        . . . .
    private:
    	StringRep* rep; // pimpl
    };
    
    
    class StringRep {
    friend class String;
    	StringRep(const char* s);
        ~StringRep();
        int count;
        char* rep;
    };
    

    Delegation 是一种弱耦合关系,在构建String时,不一定需要完成StringRep的构建。

    上述其实使用的是引用计数的方式,多个char*指针指向相同的内容时,不需要重复申请内存空间。智能指针使用的是相同的方法。

    Inheritance (继承).表示 is-a

    struct _List_node_base
    {
        _List_node_base* _M_next;
        _List_node_base* _M_prev;
    };
    template<typename _Tp>
    struct _List_node : public _List_node_base
    {
    	_Tp _M_data;
    };
    

    继承是一种强耦合关系,其构建顺序是先构建base类,再完成构建Derived类。

    Derived::Derived Derived::Derived((……): ): Base() Base() {{ …… }; };
    

    其析构顺序是先执行Derived类析构函数,再执行base类析构函数

    Derived::~Derived Derived::~Derived((……){ ){ …… ~Base()};
    

    存在继承关系时必须将父类的析构函数设置为virtual ,防止出现内存泄漏。

    Template Method--模板方法模式

    #include <iostream>
    using namespace std;
    
    class CDocument
    {
    public:
    void OnFileOpen()
    {
        // 这是个算法,每一个cout代表一个实际的操作
        cout << "dialog..." << endl;
        cout << "check file status..." << endl;
        cout << "open file..." << endl;
        Serialize();
        cout << "close file..." << endl;
        cout << "update all views..." << endl;
    }
    
    virtual void Serialize() { };
    };
    
    class CMyDoc : public CDocument
    {
    public:
        virtual void Serialize()
        {
            // 具体的业务场景去写具体的实现
            cout << "CMyDoc::Serialize()" << endl;
         }
    };
    

    Composite--复合模式

    Delegation (委托) + Inheritance (继承)

    class Component
    {
    	int value;
    public:
        Component(int val) { value = val; }
        virtual void add( Component* ) { }
    };
    
    class Primitive: public Component
    {
    public:
    	Primitive(int val): Component(val) {}
    };
    
    class Composite: public Component
    {
    	vector <Component*> c;
    public:
        Composite(int val): Component(val) { }
            void add(Component* elem) {
            c.push_back(elem);
        }
    …
    };
    

    应用场景:如文件系统中,每一个文件夹内部既可以包含具体文件,有可以包含文件夹。

    Observer--观察者模式

    class Subject
    {
        int m_value;
        vector<Observer*> m_views;
    public:
        void attach(Observer* obs)
        {
        	m_views.push_back(obs);
        }
        void set_val(int value)
        {
            m_value = value;
            notify();
        }
        void notify()
        {
            for (int i = 0; i < m_views.size(); ++i)
            m_views[i]->update(this, m_value);
        }
    };
    
    class Observer
    {
    public:
    	virtual void update(Subject* sub, int value) = 0;
    };
    

    Prototype --原型模式

    Prototype模式提供了一个通过已存在对象进行新对象创建的接口(Clone)

    #include <iostream.h>
    enum imageType
    {
    	LSAT, SPOT
    };
    
    class Image
    {
    public:
        virtual void draw() = 0;
        static Image *findAndClone(imageType);
    protected:
        virtual imageType returnType() = 0;
        virtual Image *clone() = 0;
        static void addPrototype(Image *image)
        {
        	_prototypes[_nextSlot++] = image;
        }
    private:
        static Image *_prototypes[10];
        static int _nextSlot;
    };
    
    Image *Image::_prototypes[];       //申请空间存储原型类
    int Image::_nextSlot;
    
    Image *Image::findAndClone(imageType type)
    {
        for (int i = 0; i < _nextSlot; i++)
        if (_prototypes[i]->returnType() == type)
        	return _prototypes[i]->clone();
    }
    
    class LandSatImage: public Image
    public:
    	imageType returnType() {
        	return LSAT;
        }
    	void draw() {
    	cout << "LandSatImage::draw " << _id << endl;
    	}
    
        Image *clone() {
        	return new LandSatImage(1);
        }
    protected:
    	LandSatImage(int dummy) {
    		_id = _count++;
    	}
    private:
    	static LandSatImage _landSatImage;
    	LandSatImage() {
    		addPrototype(this);
    	}
        int _id;
        static int _count;
    };
    
    LandSatImage LandSatImage::_landSatImage;
    int LandSatImage::_count = 1;
    
  • 相关阅读:
    Beta冲刺 第二天
    Beta冲刺 第一天
    实验十一 团队项目设计完善&编码测试
    实验十 软件系统详细设计与概要设计的改进
    实验九 FBG 团队项目需求改进与系统设计
    实验八 <FBG> 基于原型的团队项目需求调研与分析
    实验七 《FBG》—-小学生课后习题答案原型设计
    实验五 <FBG>团队亮相
    Windows zip版本安装MySQL
    redis安装与简单实用
  • 原文地址:https://www.cnblogs.com/maybe-fl/p/15490775.html
Copyright © 2020-2023  润新知