《C++面向对象高级开发》上篇

1.C++ 编程简介

你该具备怎样的基础

  • 曾经学过某种程序语言(C语言最佳)
    • 变量(variables)
    • 类型(types):int,float,char,struct. ⋅ ⋅ ⋅ \cdot\cdot\cdot
    • 作用域(scope)
    • 循环(loops)
    • 流程控制
  • 知道一个程序需要编译、链接才能被执行
  • 知道如何编译和链接(如果建立一个可执行程序)

我们的目标

  • 培养正规、大气的编程习惯
    以良好的方式编写C++ class(基础对象)
    • class without pointer members
      • Complex
    • class with pointer members
      • String
  • 学习Classes之间的关系(面向对象)
    • 继承(inheritance)
    • (composition)
    • (delegation)

2.头文件与类的声明

延伸文件名(extension file name)不一定是.h或.cpp,也可能是.hpp或其他或甚至无延伸名。

Header(头文件)中的防重复声明

complex.h

#ifndef __COMPLEX__
#define __COMPLEX__


#endif

class的声明

由于在方法中可能会使用到自定义类型,需要提前声明,这种声明为前置声明

class template(模板)简介

template<typename T>
class complex
{
public:
	complex (T r = 0,T i = 0):re(r),im(i){} 
	compplex& operator += (const complex&);
	T real () const {return re;}
	T imag () const {return im;}
private:
	T re,im;

	friend complex& __doapl(complex*,const complex&);
};
// 调用
{
	complex<duuble> c1(2.5,1.5);
	complex<int> c1(2,5);
}

3.构造函数

不带指针的多半不会写析构函数。

  • 方式一:

    complex (double r = 0,double i = 0):re(r),im(i){}  
    
  • 方式二:

    complex (double r = 0,double i = 0){ re = r;im =i;}
    

注意:虽然二者实现的效果一致,但是方式一效率高于方式二。这种特殊的写法只有构造函数有:re®,im(i),被称为(初值列,初始列)`

inline(内联)函数

函数太复杂没办法inline,以空间换时间。最终是否inline由编辑器决定。

(构造函数)可以有很多个——函数重载

常常发生在构造函数。
其实在代码中看到是重名函数,但是编译器会进行重新编码,命名成不同名称的名称,这表明实际上不会有真正重名的两个函数。

4.参数传递与返回值

ctor(构造函数)被放在private里面

类被创建构造函数就会被调用,以至于该类不能被创建;这种特性可用于设计模式中(单例模式)。

const member functions(常量成员函数)

在不改变数据内容的函数后面添加const修饰。

参数传递:pass by value vs. pass by reference(to const)

建议:参数尽量传引用,效率更高,但传入的引用不需要改变,添加const进行修饰。

返回值传递:return by value vs. return by reference(to const)

建议:返回值尽量返回引用,效率更高,返回值不发生改变添加const进行修饰。

friend(友元)

友元自由取得friend的private成员,友元打破了面向对象的封装。

相同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;}
private:
	double re,im;
};
{
	complex c1(2,1);
	complex c2;

	c2.func(c1); //查看该方法
}

class body外的各种定义:
什么情况下可以pass by reference
什么情况下可以return by reference

5.操作重载与临时对象

operator overloading(操作符重载1,成员函数)this

所有的成员函数都隐藏一个this参数,但是在本成员函数中不能添加。

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

inline complex& complex::operatpr += (const complex& r)
{
	return __doapl(this,r);
}

//调用
{
	complex c1(2,1);
	complex c2(5);

	c2 += c1;
}

return by reference语法分析

传送者无要知道接收者是以reference形式接收。

c2 += c2 += c1;

class body之外的各种定义(definitions)

inline double imag(const complex& x)
{
	return x.imag();
}
inline double real(const complex& x)
{
	return x.real();
}

operator overloading(操作符重载2,非成员函数)无this

需求:

{
	complex c1(2,1);
	complex c2;

	c2 = c1 + c2;
	c2 = c1 + 5;
	c2 = 7 + c1;
}

为了应对client的三种可能用法,这儿对应实现了三个函数。
下面这些函数决不可return by reference,因为它们返回的必定是个local object(临时对象).


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,const double y)
{
	return complex (real(x) + y,imag(x) + imag(y));
}
inline complex operator + (dobule x,const complex& y)
{
	return complex (x + real(y),imag(x) + imag(y));
}

取反重载

inline complex
operator + (const complex& x)
{
	return x;
}
inline complex
operator -(const complex& x)
{
	return complex(-real(x),-imag(x));
}

共轭复数

inline complex
conj(const complex& x)
{
	return complex(real(x),-imag(x));
}

输出

ostream&
operator << (ostream& os,const complex x)
{
	return os<<'('<<real(x)<<','<<imag(x)<<')';
}

6.大三函数:拷贝构造,拷贝复制,析构

只要类中带指针,就不能使用默认的拷贝构造和拷贝复制,必须进行重写。

int main()
{
	String s1();
	String s2("hello");

	String s3(s1);//拷贝构造
	cout<<s3<<endl;
	s3 = s2;//拷贝复制
	cout<<s3<<endl;
}

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;
};

ctor和dtor(构造函数和析构函数)

定义:
注意:申请空间时使用[],与之同时删除的时候也需要使用[]。

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 = '\0';
	}
}

inline
String::~String()
{
	delete[] m_data;
}

调用:

{
	String s1();
	String s2("hello");

	String* p = new String("hello");
	delete p;
}

class with pointer members 必须有copy ctor(拷贝构造)和copy op=(拷贝赋值)

copy ctor(拷贝构造函数)
inline
String::String(const String& str)
{
	m_data = new char[strlen(str.m_data)+ 1];
	strcpy(m_data,str.m_dta); 
}

调用

{
	String s1("hello");
	String s2(s1);
	String s2 = s1;
	//注意,第2行和第3行实现的结果是一样的
}
copy assignment operator(拷贝赋值函数)

先删除,然后申请空间,最后进行赋值

inline
String& String::operator=(const String& str)
{
	if(this == &str)//检测自我赋值(功力深厚的人才会想到,大家风袖)
		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;
}

调用

{
	String s1("hello ");
	String s2(s1);
	s2 = s1;
}
一定要在operator=中检查是否self assignment

进行拷贝赋值第一件事就是delete,如果两个相同指针相互赋值,则内容已经被清空,无法进行最终的完整赋值。

7.堆,栈与内存管理

output函数

重载<<>>不能用成员函数,只能写普通函数;写成成员函数调用符号在右边,和常规的调用发生冲突,因此写成普通函数调用在左边,和常规保持一致。

#include<iostream.h>
ostream& operator<<(ostream& os,const String& str)
{
	os << str.get_c_str();
	return os;
}
//调用
{
	String s1("hello ");
	cout<<s1;
}

所谓stack(栈),所谓heap(堆)

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

Heap,或谓system heap,是指由操作系统提供的一块global内存空间,程序可分动态分配(dynamic allocated)后其中获得若干空间(blocks)。(使用new动态取得)

class Complex{...};
...
{
	Complex c1(1,2);//c1所占用的空间来自stack
	Complex* p = new Complex(3);//Complex(3)是动态获得的,空间来自heap,需要手动进行释放空间
}

stack objects的生命期

class Complex{...};
...
{
Complex c1(1,2);
}

c1便是所谓的stack object,其生命在作用域(scope)结束之后结束。
这样作用域内的object,又称为auto object,因为它会被[自动]清理。

static local objects的生命期

class Complex{...};
...
{
	static Complex c2(1,2);
}

c2便是所谓的static object,其生命在作用域(scope)结束之后仍然存在,知道整个程序结束。

global objects的生命期

class Complex{...};
...
Complex c3(1,2);

int main()
{
...
}

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

heap objects的生命期

正确写法
class Complex{...};
...
{
	Complex* p = new Complex;
	...
	delete p;
}

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

错误写法
class Complex{...};
...
{
	Complex* p = new Complex;

}

以上出现内存泄漏(memory leak),因为当作用域结束,p所指的heap object仍然存在,但指针p的生命却结束了,作用域之外再也看不到p(也就是没进行delete操作)。

new:先分配memory,在调用ctor

Complex* pc = new Complex(1,2);

new编译器转化为:

Complex *pc;
//1.分配内存
void* mem = operator new(sizeof(Complex));	//分配内存(其内部调用malloc(n))
//2.转型(类型转换)
pc = static_cast<Complex*>(mem);			//转型
//3.构造函数
pc->Complex::Complex(1,2);					//构造函数

delete:先调用dtor,再释放memory

String* ps = new String("Helloo");
...
delete ps;

delete编译器转化为:

//1.析构函数
String::~String(ps);						//析构函数
//2.释放内存
operator delete(ps);						//释放内存(其内部调用free(ps))

动态分配所得的内存块(momory block),in VC(重点)

在这里插入图片描述

前后红色的为cookie,使用cookie记录存储的开始和结束。灰色部分为调试模式下,与之对应的是非调试模式下。
在其他编译器内存分配很像,但是大小是不一样的。
在vc以下所有的内存块大小一定是16的倍数(16的倍数最后4位都是0,可以借助最后一位使用)。
cookie有什么用,因为在最后回收的时候,我们只给一个指针,系统没办法确定多大空间。
(左一)其中64的16进制为40,41借助最后一位来表示该内存块空间的状态(是否给出去,或者回收回来),1表示系统已经给出去,对我们的程序而言是获得了。
(左二)其中16的16进制为10,11借助最后一位来表示该内存块空间系统已经给出去了,对我们的程序而言是获得了。

动态分配所得的array,in VC(重点)

在这里插入图片描述
一个复数两个double,所以3个复数共6个double,51h为上下cookie,3记录数组的大小(整数类型,4个字节)
奇数个为调试模式,偶数个为非调试模式。
左三左四的箭头为指针。

array new 一定要搭配array delete

在构造函数和析构函数中我们说到申请空间时使用[],与之同时删除的时候也需要使用[],要不然会发生内存泄露,此处我们说明为什么会。
正确做法:

String* p = new String[3];
...
delete[] p;//唤起3次dtor

在这里插入图片描述
错误做法:

String* p = new String[3];
...
delete p;//唤起1次dtor

在这里插入图片描述
差别点:
因为都含有cookie,所有两种方式都能够被删除,但是如果不进行搭配,正确做法进行删除时编译器能够知道需要进行三次删除,调用3次析构函数;而错误做法编译器不知道要删除的是数组,所以只会调用1次析构函数,造成除第一个元素请删除外,其他元素内存组成泄露

编程实例

头文件

class String
{
public:
	String(const char* cstr = 0);
	String(const String& str);	//拷贝构造
	String& operator=(const String& str);	//拷贝赋值
	~String();
private:
	char* m_data;
}

实现(对应array new和array delete)

inline //尽量让我们的函数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 = '\0';
	}
}

inline
String::String(const String& str)
{
	m_data = new char[strlen(str.m_data)+1];
	strcpy(m_data,str.m_data);
}

inline
String& String::operatpr=(const String& str)//引用
{
	if(this == &str)//取地址
		return *this; 
	delet[] m_data;
	m_data = new char[strlen(str.m_data)+1];
	strcpy(m_data,str.m_data);
	return *this;//可以使用void无返回,但是在连串赋值的情况下会出错
}

inline
String::~String()
{//一般关闭窗口或者关闭问题,没有则不需要
	delete[] m_data;
}

注意: 1.申请空间记得+1,为字符串的结束符; 2.拷贝赋值函数无返回值,在连串赋值的时候会出错。

8.类模板,函数模板,及其他

进一步补充:static

静态成员函数没有this pointer(this指针)

调用static的函数方法有二:

  1. 通过object调用
  2. 通过cass name调用
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()
{
	Account::set_rate(5.0);
	
	Account a;
	a.set_rate(7.0);
}
进一步补充:把ctors放在private里

Singleton(单例模式),只存在一个特定的对象a

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

调用方式:A::getInstance().setup();

上面的类即使没有被创建,a对象依然存在,这样会导致浪费内存;因此进行改进上面的方式。

class A{
public:
	static A& getInstance();
	setup()	{...}
private:
	A();
	A(const A& rhs);
	static A a;
	...
};
A& A::getInstance()
{
	static A a;
	return a;
}
进一步补充:cout
class _IO_ostream_withassign
	: public ostream{
...
};

extern _IO_ostream_withassign cout;

class ostream: virtual public ios
{
public:
	ostream& operator<<(char c);
	ostream& operator<<(unsigned char c){return (*this)<<(char)c;}
	ostream& operator<<(signed char c){return (*this)<<(char)c;}
	ostream& operator<<(const char *s);
	ostream& operator<<(const unsigned  char *s);
	ostream& operator<<(const unsigned char *s)
	{return (*this)<<(const char*)s;}
	ostream& operator<<(const signed char *s);
	{return (*this)<<(const char*)s;}
	ostream& operator<<(const void *p);
	ostream& operator<<(int n);
	ostream& operator<<(unsigned int n);
	ostream& operator<<(long n);
	ostream& operator<<(unsigned long n);
	...
}
进一步补充:class template,类模板

实现

template<typename T>
class complex
{
public:
	complex(T r = 0,T i = 0)
	  : re(r),im(i)
	 { }
	 complex& operator += (const complex&);
	 T real() const {return re;}
	 T imag() const {return im;}
private:
	T re, im;
	
	friend complex& __doapl(complex*, const complex&);
};

调用(这样会出现两份代码)

{
	complex<double> c1(2.5, 1.5);
	complex<int> c2(2, 6);
	...
}
进一步补充:function template,函数模板

举例(此处不需要指明类型,编译器会对function template进行实参推导)

class stone
{
public:
	stone(int w,int h,int we)
	 : _w(w),  _h(h),  _weight(we)
		{ }
	bool operator< (const stone& rhs) const ///
		{ return _weight < rhs._weight; }
private:
	int _w, _h, _weight;
};

stone r1(2,3), r2(3,3), r3;
r3 = min(r1, r2);

实现

template <class T>
inline
const T& min(const T& a, const T& b)
{
	return b < a ? b : a;
}

实参推导的结果,Tstone,于是调用stone::operator<
注意:typenameclass都可以使用。

进一步补充:name

格式:

namespace 命名
{
	...
}

调用方式:

  • using directive
#include <iostream.h>
using namespce std;//全部打开

int main()
{
	cin << ...;
	cout << ...;

	return 0;
}
  • using declaration
#include <iostream.h>
using std::cout;//使用cout不需要全名

int main()
{
	std::cin << ...;
	cout << ...;

	return 0;
}
  • 每个加入
#include <iostream.h>

int main()
{
	std::cin << ...;
	std::cout << ...;

	return 0;
}
更多细节要深入
  • operator type() const;
  • explicit complex(…): initialization list{}
  • pointer-like object
  • function-like object
  • Namespace
  • template specialization
  • Standard Library
  • variadic template(since C++11)
  • move ctor (since C++11)
  • Rvalue reference(since C++11)
  • auto(since C++11)
  • lambda(since C++11)
  • range-base for loop(since C++11)
  • unordered containers(since C++11)

9. 组合与继承

  • Inheritance(继承)
  • Composition(复合)
  • Delegation(委托)

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(); }
	//deque 是两端可进出,queue是末端进前端出(先进后出)
	void push(const value_type& x) { c.push_back(x); }
	void pop() { c.pop_front(); }
};

在这里插入图片描述
图表:一个里面有另一个东西(黑色箭头表示有东西)
替换上面的c定义代码操作:
Adapter

template <class T>
class queue{
	...
protected:
	deque<T>  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(); }
	//deque 是两端可进出,queue是末端进前端出(先进后出)
	void push(const value_type& x) { c.push_back(x); }
	void pop() { c.pop_front(); }
};

背景知识:deque是双端队列,queue是队列。

从内存角度分析

注意下侧是层层递进关系。

sizeof :40
template <class T>
class queue{
protected:
	deque<T> c;
...
};
↓↓↓
sizeof:16*2+4+4
template <class T>
class deque{
protected:
	Itr<T> start;
	Itr<T> finish;
	T** map;
	unsigned int map_size;
};
↓↓↓
sizeof:4*4
template <class T>
struct Itr{
	T* curr;
	T* first;
	T* last;
	T** node;
...
};
Composition(复合)关系下的构造和析构

在这里插入图片描述

构造由内而外

Container的构造函数首先调用Component的default构造函数,然后才执行自己。

Container::Container(...):Component(){...};

先执行Component,然后执行Container。

析构由外而内

Container的析构函数首先执行自己,然后才调用Component的析构函数。

Container::~Container(...):{...~Component()};

先执行~Container,然后执行Component。

10.Delegation(委托),Composition by reference.

//file String.hpp
class StringRep;
class String{
public:
	String();
	String(const char* s);
	String(const String& s);
	String &operator=(const String* s);
	~String();
.....
private:
	StringRep* rep;//pimp1(见下面定义)
};
//file String.cpp
#include "String.hpp"
namespace{
friend class String{
	StringRep(const char* s);
	~StringRep();
	int count;
	char* rep;
};
}

在这里插入图片描述
在这里插入图片描述
StringRep* rep在类中用指针相连,生命周期不一致。
桥接(Bridge)模式,又称Handle/Body模式
在左手边对外不变进行调用(客户端),右边单独进行实现(服务端)。

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;
};

struct _List_node: public _List_node_base:结构体继承,C++有三种(public,private,protected)继承方式,最有用的是public继承。
在这里插入图片描述
继承最有价值是和虚函数搭配。

Inheritance(继承)关系下的构造和析构

在这里插入图片描述
注意:只要你的类以后或者现在要变为父类,就必须把它的析构函数设置为virtual。

构造由内而外

Derived的构造函数首先调用Base的default构造函数,然后才执行自己。

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

先执行Base,然后执行Derived。

析构由外而内

Derived的析构函数首先执行自己,然后才调用Base的析构函数。

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

先执行~Derived,然后执行Base。

Inheritance(继承)with virtual functions(虚函数)

non-virtual函数:你不希望Derived class重新定义(override,覆盖)它。
virtual函数:你希望derived class重新定义(override,覆盖)它,且你对它已有默认定义。
pure virtual函数:你希望Derived class一定要重新定义(override覆盖)它,你对它没有默认定义(其实纯虚函数有定义,此处不进行说明)。

class Shape{
public:
	virtual void draw() const = 0;			//pure virtual
	virtual void error(const std::string& msg);	//impure virtual
	int objectID() const;					//non-virtual
	...
};

class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };

模板方法模式(设计模式之一),虚函数的经典用法之一。
在这里插入图片描述

myDoc.opFileOpen();
//实际调用规则:
CDocument::OnFileOpen(&myDoc);

代码示例,对应上策的示意图:

#include<iostream>
using namespace std;

class CDocument
{
public:
	void OnFileopen()
	{//这是个算法,每个count输出代表一个实际动作
		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 CMDoc:public CDocument
{
public:
	virtual void Serialize()
	{//只有应用程序本身才知道如如何1读取自己的文件格式
		cout<<"CMDoc::Serialize()"<<endl;
	}
};

int main()
{
	CMyDoc myDoc();	//假设对应(File/Open)
	myDoc.onFileOpen();
}

Inheritance+Composition关系下的构造和析构

对应的作业,判断谁先谁后。
在这里插入图片描述

Delegation(委托)+Inheritance(继承)

应用场景:对应于一个更改,其余部分跟着更改。

class Observer
{
public:
	virtual void update(Subject* sub,int value)=0;
};

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);
	}
}

Composite(组合设计模式)
应用场景:大套小,小套大。目录里面可以放文件,也可以继续放目录。
我们此处将Primitive认为是文件,Composite为目录。好吧我承认我不太理解。
在这里插入图片描述

对应代码

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

	void add(Component* elem){
		c.push_back(elem);
	}
}
class Primitive:Public Component
{
public:
	Primitive(int val):Component(val){}
}

Prototype(原型模式)
应用场景:该接口(写在前面)用于创建未来对象(之后才开始创建)的克隆。无法new(不知道对象名称)。
在这里插入图片描述
LSAT:指的是静态。
-:表示为私有。通过构造函数将自身放到父类上去。
clone:用于new自己并返回。
《Design Patterns Explained Simply》
父类代码:

#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;/
	//As each subclass of Image is declared,it registers its prototype
	static void addPrototype(Image * image)
	{
		_prototypes[_nextSlot++] = image;
	}
private:
	//addPrototype() saves each registered prototype here
	static Image* _prototypes[10];///
	static int _nextSlot;///
};
Image *Image::_prototypes[];//定义(给内存)
int Image::_nextSlot;//定义(给内存)

//Client calls this public static member function when it needs instance of an Image subclass
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;
	}
	//when clone() is called,call the one-argument ctor with a dummy arg
	Image *clone(){
		return new LandSatImage(1);//new自己
	}
protected:
//This is only called from clone()
	LandSatImage(int dummy){//(为了区分原有的构造函数,为了区分默认添加一个无用参数)
		_id = _count++;
	}
private:
//Mechanism for initializing an Image subclass- this causes the default ctor to be called,which registers the subclass's prototypr
	static LandSatImage LandSatImage;//✔✔静态的自己
//This is only called when the private static data
	LandSatImage()//把自己放到父类上去
	{
		addPrototype(this);
	}
//Norminal "state" per instace mechanism
itn _id;
static int _count;
};
//Register the subclass's prototype
LandSatImage LandSatImage::_landSatImage::
//Initialize the "state" per insatnce mechanism
int LandSatImage::count = 1;

后记

虽然更新完了,但是好多关于设计模式方面的思想并没有完全理解,还得多多理解才好。

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值