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;