笔记: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;
原文地址:https://www.cnblogs.com/maybe-fl/p/15490775.html