常用的C++面向对象编程模板

1. 一些基础的规则

  • 类中对于不会改变数据的函数,在后面加上const(常量成员函数)—— 方便常量对象的调用。
  • 一般没有指针的类不需要析构函数。
  • 同一个类的各个对象之间互为友元。
  • 参数和返回值尽量采用引用。
  • 数据中有指针的类必须要重写拷贝构造和拷贝赋值等,如果一个类需要自定义析构函数,那么可以肯定它也需要定义拷贝赋值运算符和拷贝构造函数。
  • 静态成员函数用于处理静态数据。
  • 基类的析构函数要写成virtual。

2. 日期类

2.1 日期类头文件

#ifndef DATE_H
#define DATE_H

#ifndef _MSC_VER
#define NOEXCEPT noexcept
#else
#define NOEXCEPT
#endif

#include <iostream>
#include <vector>

class Date {
	friend bool operator ==(const Date& lhs, const Date& rhs);
	friend bool check(const Date &d);
	friend std::ostream& operator <<(std::ostream& os, const Date& d);

public:
	typedef std::size_t Size;						// 定义类型别名

	Date() = default;                               // 这个默认构造函数是每一个类都要写吗
	explicit Date(Size days);						// 防止隐式转换
	Date(Size d, Size m, Size y) : day(d), month(m), year(y) {}              // 构造函数
	Date(std::istream &is, std::ostream &os);       // 构造函数
	// 拷贝构造函数
	Date(const Date& d);
	// 移动构造函数
	Date(Date&& d) NOEXCEPT;

	// 拷贝赋值运算符
	Date& operator= (const Date& d);
	// 移动赋值运算符
	Date& operator= (Date&& rhs) NOEXCEPT;

	// 析构函数
	~Date() { std::cout << "destroying\n"; }

	// 成员函数
	Size toDays() const;  //
	Date& operator +=(Size offset);
	Date& operator -=(Size offset);

private:
	Size    day = 1;
	Size    month = 1;
	Size    year = 0;

};

// 定义静态变量
static const Date::Size YtoD_400 = 146097;    //365*400 + 400/4 -3 == 146097
static const Date::Size YtoD_100 = 36524;    //365*100 + 100/4 -1 ==  36524
static const Date::Size YtoD_4 = 1461;    //365*4 + 1          ==   1461
static const Date::Size YtoD_1 = 365;    //365

// 普通年
static const std::vector<Date::Size> monthsVec_n =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

// 闰年
static const std::vector<Date::Size> monthsVec_l =
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

// 一些全局函数的声明
std::ostream& operator <<(std::ostream& os, const Date& d);
std::istream& operator >>(std::istream& is, Date& d);
int operator - (const Date& lhs, const Date& rhs);
bool operator ==(const Date& lhs, const Date& rhs);
bool operator !=(const Date& lhs, const Date& rhs);
bool operator < (const Date& lhs, const Date& rhs);
bool operator <=(const Date& lhs, const Date& rhs);
bool operator  >(const Date& lhs, const Date& rhs);
bool operator >=(const Date& lhs, const Date& rhs);
Date operator - (const Date& lhs, Date::Size  rhs);                    
Date operator  +(const Date& lhs, Date::Size  rhs);               // 注意+的类型是Data::Size

bool check(const Date &d);                      // 函数声明
bool isLeapYear(Date::Size y);					// 函数声明

// 将其中一些全局函数进行定义
inline 
bool check(const Date &d)
{
	if (d.month == 0 || d.month > 12)
		return false;
	else
	{
		//    month == 1 3 5 7 8 10 12
		if (d.month == 1 || d.month == 3 || d.month == 5 || d.month == 7 ||
			d.month == 8 || d.month == 10 || d.month == 12)
		{
			if (d.day == 0 || d.day > 31) return false;
			else
				return true;
		}
		else
		{
			//    month == 4 6 9 11
			if (d.month == 4 || d.month == 6 || d.month == 9 || d.month == 11)
			{
				if (d.day == 0 || d.day > 30) return false;
				else
					return true;
			}
			else
			{
				//    month == 2
				if (isLeapYear(d.year))
				{
					if (d.day == 0 || d.day > 29)  return false;
					else
						return true;
				}
				else
				{
					if (d.day == 0 || d.day > 28)  return false;
					else
						return true;
				}
			}
		}
	}
}

inline 
bool isLeapYear(Date::Size y)
{
	if (!(y % 400))
	{
		return true;
	}
	else
	{
		if (!(y % 100))
		{
			return false;
		}
		else
			return !(y % 4);
	}
}

#endif

2.2 日期类执行文件

#include "Data.h"
#include <algorithm>

Date::Date(Size days)
{
	// calculate the year
	Size y400 = days / YtoD_400;
	Size y100 = (days - y400 * YtoD_400) / YtoD_100;
	Size y4 = (days - y400 * YtoD_400 - y100 * YtoD_100) / YtoD_4;
	Size y = (days - y400 * YtoD_400 - y100 * YtoD_100 - y4 * YtoD_4) / 365;
	Size d = days - y400 * YtoD_400 - y100 * YtoD_100 - y4 * YtoD_4 - y * 365;
	this->year = y400 * 400 + y100 * 100 + y4 * 4 + y;

	// check if leap and choose the months vector accordingly
	std::vector<Size>currYear
		= isLeapYear(this->year) ? monthsVec_l : monthsVec_n;

	// calculate day and month using find_if + lambda
	Size D_accumu = 0, M_accumu = 0;
	// @bug    fixed:  the variabbles above hade been declared inside the find_if as static
	//                 which caused the bug. It works fine now after being move outside.

	std::find_if(currYear.cbegin(), currYear.cend(), [&](Size m)
	{

		D_accumu += m;
		M_accumu++;

		if (d < D_accumu)
		{
			this->month = M_accumu;
			this->day = d + m - D_accumu;

			return true;
		}
		else
			return false;
	});
}

Date::Date(std::istream &is, std::ostream &os)
{
	is >> day >> month >> year;

	if (is)
	{
		if (check(*this)) return;
		else
		{
			os << "Invalid input! Object is default initialized.";
			*this = Date();
		}
	}
	else
	{
		os << "Invalid input! Object is default initialized.";
		*this = Date();
	}

}

// 拷贝构造函数
Date::Date(const Date &d) :
	day(d.day), month(d.month), year(d.year)
{}

// 移动构造函数
Date::Date(Date&& d) NOEXCEPT :
	day(d.day), month(d.month), year(d.year)
{
	std::cout << "copy moving";
}

// 赋值构造函数
Date &Date::operator= (const Date &d)
{
	this->day = d.day;
	this->month = d.month;
	this->year = d.year;

	return *this;
}

// 移动赋值函数
Date &Date::operator =(Date&& rhs) NOEXCEPT
{
	if (this != &rhs)
	{
		this->day = rhs.day;
		this->month = rhs.month;
		this->year = rhs.year;
	}
	std::cout << "moving =";

	return *this;
}

// conver to days
Date::Size Date::toDays() const
{
	Size result = this->day;

	// check if leap and choose the months vector accordingly
	std::vector<Size>currYear
		= isLeapYear(this->year) ? monthsVec_l : monthsVec_n;

	// calculate result + days by months
	for (auto it = currYear.cbegin(); it != currYear.cbegin() + this->month - 1; ++it)
		result += *it;

	// calculate result + days by years
	result += (this->year / 400)      * YtoD_400;
	result += (this->year % 400 / 100)  * YtoD_100;
	result += (this->year % 100 / 4)    * YtoD_4;
	result += (this->year % 4)        * YtoD_1;

	return result;
}


Date &Date::operator +=(Date::Size offset)
{
	*this = Date(this->toDays() + offset);
	return *this;
}

Date &Date::operator -=(Date::Size offset)
{
	if (this->toDays() > offset)
		*this = Date(this->toDays() - offset);
	else
		*this = Date();

	return *this;
}


std::ostream&
operator <<(std::ostream& os, const Date& d)
{
	os << d.day << " " << d.month << " " << d.year;
	return os;
}

std::istream&
operator >>(std::istream& is, Date& d)
{
	if (is)
	{
		Date input = Date(is, std::cout);
		if (check(input))    d = input;
	}
	return is;
}


int operator -(const Date &lhs, const Date &rhs)
{
	return lhs.toDays() - rhs.toDays();
}


bool operator ==(const Date &lhs, const Date &rhs)
{
	return (lhs.day == rhs.day) &&
		(lhs.month == rhs.month) &&
		(lhs.year == rhs.year);
}


bool operator !=(const Date &lhs, const Date &rhs)
{
	return !(lhs == rhs);
}


bool operator < (const Date &lhs, const Date &rhs)
{
	return lhs.toDays() < rhs.toDays();
}


bool operator <=(const Date &lhs, const Date &rhs)
{
	return (lhs < rhs) || (lhs == rhs);
}


bool operator >(const Date &lhs, const Date &rhs)
{
	return !(lhs <= rhs);
}


bool operator >=(const Date &lhs, const Date &rhs)
{
	return !(lhs < rhs);
}


Date operator - (const Date &lhs, Date::Size rhs)
{                                       
	Date result(lhs);
	result -= rhs;

	return result;
}


Date operator + (const Date &lhs, Date::Size rhs)
{                                       
	Date result(lhs);
	result += rhs;

	return result;
}

3. 书籍销售类

  • 类型转换函数

3.1 书籍销售类头文件

#ifndef SALE_DATA_H
#define SALE_DATA_H
#include<iostream>
#include<string>

using std::ostream;
using std::istream;
using std::string;

class Sales_data
{
// 友元声明
	friend istream& operator>>(istream&, Sales_data&); // input
	friend ostream& operator<<(ostream&, const Sales_data&); // output

	friend Sales_data operator+(const Sales_data&, const Sales_data&); // addition
	friend bool operator==(const Sales_data&, const Sales_data&);  // equal
	friend bool operator!=(const Sales_data&, const Sales_data&);  // no equal
	


// 公有成员
public:
	// 构造函数
	Sales_data() = default;                            // 保留默认构造函数
	Sales_data(const string &s, unsigned n, double p) :
		bookNo(s), unit_sold(n), revenue(p*n) { }      // 这些都是初始化,不是赋值
		
	// 拷贝构造函数(实际上不需要重新定义,因为编译器有默认的)
	Sales_data(const Sales_data &orig) :
		bookNo(orig.bookNo), unit_sold(orig.unit_sold), revenue(orig.revenue) { } 

	// Sales_data(const string& s) :Sales_data(s, 0, 0) { }  // 委托构造函数的使用
	// explicit Sales_data(const string &s) :bookNo(s) { }  // 防止隐式转换:explicit
	
	Sales_data(const string &s) :bookNo(s) { }
	Sales_data(std::istream &is);

	// 普通成员函数
	string const& isbn() const { return this->bookNo; }      // 使用this来把对象当成一个整体来访问而非直接访问对象的某个成员
	Sales_data& operator+=(const Sales_data&);              
	Sales_data& operator=(const std::string&);
	
	// 类型转换运算符的重载:上面的两种类型转换有歧义,应该声明成 explicit 的!
	explicit operator std::string() const { return bookNo; }
	explicit operator double() const { return avg_price(); }

// 私有成员
private:
	double avg_price() const;  // 返回售出书籍的平均价格
	
	// 数据成员
	string bookNo;
	unsigned unit_sold = 0;  // 总销售数量
	double revenue = 0.0;   // 总销售价格

};


// 最好只在类外部定义的地方说明inline
inline
double Sales_data::avg_price() const {
	if (unit_sold)
		return revenue / unit_sold;
	else
		return 0;
}

inline
Sales_data& Sales_data::operator+=(const Sales_data &rhs) {
	unit_sold += rhs.unit_sold;
	revenue += rhs.revenue;
	return *this;
}

#endif

3.2 书籍销售类执行文件

#include "Sales_data.h"

// 特殊构造函数
Sales_data::Sales_data(istream &is) {
	is >> *this  // 从is中读取一条交易信息然后存入this对象中
}

std::istream& operator>>(std::istream &is, Sales_data &item)
{
	double price = 0.0;
	is >> item.bookNo >> item.unit_sold >> price;
	if (is)
		item.revenue = price * item.unit_sold;
	else
		item = Sales_data();
	return is;
}

std::ostream& operator<<(std::ostream &os, const Sales_data &item)
{
	os << item.isbn() << " " << item.unit_sold << " " << item.revenue << " " << item.avg_price();
	return os;
}

Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{
	Sales_data sum = lhs;
	sum += rhs;   // 注意利用复合赋值运算符实现算术运算符,避免使用一个临时对象
	return sum;
}

bool operator==(const Sales_data &lhs, const Sales_data &rhs) {
	return lhs.isbn() == rhs.isbn() &&
		lhs.unit_sold == rhs.unit_sold &&
		lhs.revenue == rhs.revenue;
}

bool operator!=(const Sales_data &lhs, const Sales_data &rhs) {
	return !(lhs == rhs);
}

Sales_data& Sales_data::operator=(const std::string &isbn)
{
	*this = Sales_data(isbn);
	return *this;
}

4. 指针类

4.1 指针类1头文件

#ifndef MYHASPTR
#define MYHASPTR
#include <iostream>
#include <string>

using std::string;
using std::ostream;

// 定义行为像值的类
class HasPtr {
	friend bool operator<(const HasPtr&, const HasPtr&);
	friend ostream& print(ostream &os, const HasPtr &hp);
	friend void swap(HasPtr &hp1, HasPtr &hp2);

public:
	// 普通构造函数
	HasPtr(const string &s = std::string()) :
		ps(new string(s)), i(0) {
		std::cout << "调用单参或无参构造函数" << std::endl;
	}
	HasPtr(const string &s, int t) :
		ps(new string(s)), i(t) {
		std::cout << "调用两参构造函数" << std::endl;
	}
	// 拷贝构造函数
	HasPtr(const HasPtr& hp) :
		ps(new std::string(*hp.ps)), i(hp.i) {
		std::cout << "调用拷贝构造函数" << std::endl;
	}
	// 拷贝赋值运算符
	HasPtr& operator=(const HasPtr &rhs_hp) {
		std::cout << "调用赋值运算符" << std::endl;
		// 这种写法更好
		if (this != &rhs_hp) {
			string *temp_ps = new string(*rhs_hp.ps);
			delete ps;                				// 注意将原来的指针释放
			ps = temp_ps;
			i = rhs_hp.i;
		}
		return *this;
	}
	
	// 拷贝赋值运算符(2)—用值作为参数且不能是常数
	//HasPtr& operator=(HasPtr rhs_hp) {
	//	swap(*this, rhs_hp);
	//	return *this;
	//}
	
	// 析构函数
	~HasPtr() {
		delete ps;
	}
private:
	std::string *ps;
	int i;
};

inline
void swap(HasPtr &hp1, HasPtr &hp2) {
	std::cout << "调用自定义版本的swap" << std::endl;
	using std::swap;         // 为了避免使用成我们自己的版本而引起无限递归
	swap(hp1.i, hp2.i);      // 系统版本
	swap(hp1.ps, hp2.ps);    // 系统版本
}

inline
bool operator<(const HasPtr &lhs, const HasPtr &rhs) {
	std::cout << "比较" << std::endl;
	return *lhs.ps < *rhs.ps;
}

std::ostream& print(std::ostream &os, const HasPtr &hp)
{
	os << "string:" << *hp.ps << " int:" << hp.i << std::endl;
	return os;
}

#endif

4.2 指针类2头文件

// 定义行为像指针的类
class HasPtr {
	friend ostream& print(ostream &os, const HasPtr &ps);

public:
	// 普通构造函数
	HasPtr(const string &s = string()) :
		ps(new string(s)), i(0), use(new std::size_t(1)) { }

	HasPtr(std::size_t t, const std::string &s = std::string()) :
		ps(new std::string(s)), i(t), use(new std::size_t(1)) { }

	// 拷贝构造函数
	HasPtr(const HasPtr& hp) :
		use(hp.use), ps(hp.ps), i(hp.i) {	// 注意这里指针指向同一内存
		++*use;
	}
	
	// 拷贝赋值运算符
	HasPtr& operator=(HasPtr &hp)
	{
		//*ps.use++  error: ++和*优先级相同,从右向左进行,先加了地址在解引用。
		//判断原值是否已经没有指针指向
		if (--*use == 0)
		{
			delete use;
			delete ps;
		}
		ps = hp.ps;
		use = hp.use;
		i = hp.i;
		++*hp.use;

		return *this;
	}
	
	// 析构函数
	~HasPtr()
	{
		if (--*use == 0)
		{
			delete use;
			delete ps;
		}
	}

private:
	std::string *ps;
	int i;
	std::size_t *use; //用来记录有多少对象共享*ps的成员
};

// 流要输出不能是const类型
std::ostream& print(std::ostream& os, const HasPtr &ps)
{
	os << "string:" << *ps.ps << " int:" << ps.i << " use:" << *ps.use;
	return os;
}

5. string类

5.1 string类1头文件

#ifndef MYSTRING
#define MYSTRING
#include<memory>
#include<iostream>

class String {
	friend std::ostream& operator<<(std::ostream&, const String&);
public:
	String() :
		String("") {}
	String(const char *);
	String(const String&);
	String& operator=(const String&);
	~String();


	const char *c_str() const { return elements; }             // 返回一个常量指针
	size_t size() const { return end - elements; }
	size_t length() const { return end - elements - 1; }

private:
	std::pair<char*, char*> alloc_n_copy(const char*, const char*);
	void range_initializer(const char*, const char*);
	void free();

private:
	char *elements;
	char *end;
	std::allocator<char> alloc;
};

#endif // !MYSTRING

5.2 string类1执行文件

#include "String.h"
#include <algorithm>

std::pair<char*, char*>
String::alloc_n_copy(const char *b, const char *e)
{
	auto str = alloc.allocate(e - b);
	return{ str, std::uninitialized_copy(b, e, str) };    // 返回的是尾地址
}

void String::range_initializer(const char *first, const char *last)
{
	auto newstr = alloc_n_copy(first, last);
	elements = newstr.first;
	end = newstr.second;
}

String::String(const char *s)
{
	char *sl = const_cast<char*>(s);
	while (*sl)
		++sl;
	range_initializer(s, ++sl);
}

String::String(const String& rhs)
{
	range_initializer(rhs.elements, rhs.end);
	std::cout << "copy constructor" << std::endl;
}

void String::free()
{
	if (elements)
	{
		std::for_each(elements, end, [this](char &c) { alloc.destroy(&c); });   // 为什么不是指针
		alloc.deallocate(elements, end - elements);
	}
}

String::~String()
{
	free();
}

String& String::operator = (const String &rhs)
{
	auto newstr = alloc_n_copy(rhs.elements, rhs.end);
	free();
	elements = newstr.first;
	end = newstr.second;
	std::cout << "copy-assignment" << std::endl;
	return *this;
}

std::ostream& operator<<(std::ostream &os, const String &s)
{
	char *c = const_cast<char*>(s.c_str());         // 类型转换
	while (*c)
		os << *c++;
	return os;
}

5.3 string类2头文件

5.4 string类2执行文件

6. Account类

6.1 Account类执行文件 —— 拥有静态成员

#include<iostream>
class Account {
public:
	void calculate() { amount += amount * interestRate; }
	static double rate() { return interestRate; }
	static void rate(double newRate) { interestRate = newRate; }

private:
	std::string owner;
	double amount;
	static double interestRate;
	static constexpr double todayRate = 42.42;
	static double initRate() { return todayRate; }
};

double Account::interestRate = initRate();   // 类外部定义并初始化每个静态成员,把静态数据成员的定义与其他非内联函数的定义放在同一个源文件中,这样可以确保对象只被定义一次
constexpr double Account::todayRate;         // 在类外再次定义

7. StrVec类

7.1 StrVec类头文件

#ifndef MYSTRVEC
#define MYSTRVEC
#include <memory>
#include <string>
#include <initializer_list>

using std::string;
using std::allocator;
using std::pair;


class StrVec {
	friend bool operator==(const StrVec&, const StrVec&);
	friend bool operator!=(const StrVec&, const StrVec&);
	friend bool operator< (const StrVec&, const StrVec&);
	friend bool operator> (const StrVec&, const StrVec&);
	friend bool operator<=(const StrVec&, const StrVec&);
	friend bool operator>=(const StrVec&, const StrVec&);

public:
	StrVec(): // allocator成员进行默认初始化
		elements(nullptr), first_free(nullptr), cap(nullptr) {}
	StrVec(const StrVec&); // 拷贝构造函数
	StrVec& operator=(const StrVec&);    // 拷贝赋值运算符
	StrVec(std::initializer_list<std::string>);
	void range_initialize(const string *first, const string *last);
	~StrVec();    // 析构函数

	StrVec& operator=(std::initializer_list<std::string>);

	void push_back(const string&);  //  拷贝元素
	size_t size() const {
		return first_free - elements;
	}
	size_t capacity() const {
		return cap - first_free;
	}
	string* begin() const {
		return elements;
	}
	string* end() const {
		return first_free;
	}

	void reserve(size_t new_cap);
	void resize(size_t count);
	void resize(size_t count, const std::string&);

private:
	// 工具函数,被拷贝构造函数、赋值运算符和析构函数所使用
	pair<string*, string*> alloc_n_copy(const string*, const string*);
	// 销毁元素释放内存
	void free();
	// 被添加元素的函数所使用
	void chk_n_alloc() { 
		if (size() == capacity()) 
			reallocate(); 
	}
	void reallocate();     // 获得更多内存并拷贝已有元素
	void alloc_n_move(size_t new_cap);


private:
	std::string *elements;  // 指向数组首元素的指针
	std::string *first_free;  // 指向数组第一个空闲元素的指针
	std::string *cap;  // 指向数组第一个空闲元素的指针
	allocator<string> alloc;  // 分配元素
};

bool operator==(const StrVec&, const StrVec&);
bool operator!=(const StrVec&, const StrVec&);
bool operator< (const StrVec&, const StrVec&);
bool operator> (const StrVec&, const StrVec&);
bool operator<=(const StrVec&, const StrVec&);
bool operator>=(const StrVec&, const StrVec&);

#endif // !MYSTRVEC

7.2 StrVec类执行文件

#include "StrVec.h"
#include <algorithm>


StrVec::StrVec(const StrVec &rhs)
{
	// 调用alloc_n_copy分配空间以容纳与rhs中一样多的元素
	auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}

void StrVec::range_initialize(const std::string *first, const std::string *last)
{
	auto newdata = alloc_n_copy(first, last);
	elements = newdata.first;
	first_free = cap = newdata.second;
}

StrVec::StrVec(std::initializer_list<std::string> il)
{
	range_initialize(il.begin(), il.end());
}

StrVec::~StrVec()
{
	free();
}

StrVec& StrVec::operator = (const StrVec &rhs)
{
	// 调用alloc_n_copy分配空间以容纳与rhs中一样多的元素
	auto data = alloc_n_copy(rhs.begin(), rhs.end());
	free();
	elements = data.first;
	first_free = cap = data.second;
	return *this;
}

void StrVec::push_back(const std::string &s)
{
	// 确保有空间容纳新元素
	chk_n_alloc();
	// 在first_free指向的元素中构造s的副本
	alloc.construct(first_free++, s);
}

pair<string*, string*> StrVec::alloc_n_copy(const string *b, const string *e) {
	// 分配空间保存给定范围的元素
	auto data = alloc.allocate(e - b);
	return { data, std::uninitialized_copy(b,e, data) };
}

void StrVec::reallocate()
{
	// 新容量的大小
	auto newcapacity = size() ? 2 * size() : 1;
	alloc_n_move(newcapacity);
}

void StrVec::alloc_n_move(size_t new_cap)
{
	auto newdata = alloc.allocate(new_cap);			   // 返回分配内存初始地址的指针
	auto dest = newdata;
	auto elem = elements;
	for (size_t i = 0; i != size(); ++i)
		alloc.construct(dest++, std::move(*elem++));   // 注意第二个元素是数据
	free();                                            // 释放原有的内存空间
	elements = newdata;
	first_free = dest;
	cap = elements + new_cap;
}


void StrVec::free()
{
	// 不能传递给deallocate一个空指针,如果elements为0,函数什么也不做
	if (elements) {
		// 逆序销毁元素
		for (auto p = first_free; p != elements;)
			alloc.destroy(--p);  // 销毁元素
		alloc.deallocate(elements, cap - elements);   // 销毁内存
	}
}


void StrVec::reserve(size_t new_cap)
{
	if (new_cap <= capacity()) return;
	alloc_n_move(new_cap);
}


void StrVec::resize(size_t count)
{
	resize(count, std::string());
}


void StrVec::resize(size_t count, const std::string &s)
{
	if (count > size()) {
		if (count > capacity()) reserve(count * 2);
		for (size_t i = size(); i != count; ++i)
			alloc.construct(first_free++, s);
	}
	else if (count < size()) {
		while (first_free != elements + count)
			alloc.destroy(--first_free);
	}
}

bool operator==(const StrVec &lhs, const StrVec &rhs)
{
	return (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()));
}

bool operator!=(const StrVec &lhs, const StrVec &rhs)
{
	return !(lhs == rhs);
}

bool operator<(const StrVec &lhs, const StrVec &rhs)
{
	return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}

bool operator>(const StrVec &lhs, const StrVec &rhs)
{
	return rhs < lhs;
}

bool operator<=(const StrVec &lhs, const StrVec &rhs)
{
	return !(rhs < lhs);
}

bool operator>=(const StrVec &lhs, const StrVec &rhs)
{
	return !(lhs < rhs);
}

StrVec& StrVec::operator=(std::initializer_list<std::string> il)
{
	*this = StrVec(il);
	return *this;
}

8. Quote类

8.1 Quote类头文件 —— 继承的使用

父类中的所有函数子类都可以通通调用,根据是否需要子类进行重新定义可以分为以下三类:

  • non-virtual函数:不希望子类被重新定义
  • virtual函数:希望子类重新定义(有默认的定义)
  • pure virtual函数:希望子类一定要重新的定义(没有默认的定义)
#pragma once
#ifndef QUOTE
#define QUOTE
#include <iostream>

class quote
{
public:
	quote() = default;
	quote(std::string &book, double p) :
		bookNo(book), price(p) { }
	virtual double net_price(std::size_t sz)const     //虚函数
	{
		return sz * price;
	}

protected:                                            //因为派生类也需要有一份这样的成员所以定义为protected
	double price = 0.0;

private:
	std::string bookNo = "";
};



class disc_quote : public quote                            //抽象基类,抽象出所有折扣的类
{
public:
	disc_quote() = default;
	disc_quote(std::string &book, double p, double disc, std::size_t num) :   //构造函数初始化quote类时必须调用quote类的构造函数
		quote(book, p), discount(disc), limit_num(num) { }
	double net_price(std::size_t sz)const = 0;             // 定义为纯虚函数

protected:                             //定义成保护的成员,因为要被派生类继承。且每个派生类都有自己独一无二的成员
	double discount = 0.0;           //表示折扣
	std::size_t limit_num = 0;       //表示限定数量
};





class bulk_quote : public disc_quote                        //继承抽象基类,有一种具体的折扣策略
{
public:
	bulk_quote() = default;
	bulk_quote(std::string &book, double p, double disc, std::size_t num) :
		disc_quote(book, p, disc, num) { }
	double net_price(std::size_t sz)const override   //不加const错误,不是同一个函数了
	{
		if (sz > limit_num)
			return (sz - limit_num) * price * discount + limit_num * price;
		else
			return sz * price;
	}

private:
};

#endif

9. 静态实现单例设计模式

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

A& A::getInstance()  //当没有人使用A时,她就不会被创建,一旦有人使用,就会被创建,并且不会随着函数消失而消失
{
    static A a;      // 静态成员,一个类只有一个            
    return a;
}

A::getInstance().setup();//通过这种方式来调用唯一的类的函数

10. 委托(pImpl)

委托关系(通过指针的复合,一个类A中有另一个类B的指针,A中要有big three),通过一个Handle类存有另一个实现类的指针,从而将其调用,这样的方法也会经常用到,实现类的变动不会影响Handle类。

10.1 pImpl头文件

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

10.2 pImpl执行文件

#include "String.hpp"
namespace {
class StringRep {
	friend class String;
	StringRep(const char* s);
	~StringRep();
	int count;
	char* rep;
};
}

String::String() {...}
...

10.3 委托+继承类模板

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

10.4 文件系统类

class Component
{
	int value;
public:
	Component(int val) { value = val; }
	virtual void add(Component*) { }                 // 不能写成纯虚函数,因为Primitive无法定义
}

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(elem);
	}
	...
}

11. 分数类

  • 类型转换函数。
class Fraction
{
public:
	explicit Fraction(int num, int den = 1)          // 可以实现转换
	  : m_numerator(num), m_denominator(den)
	{ cout << m_numerator << ' ' << m_denominator << endl; }
	
 	operator double() const {                        // 转换函数
      return (double)m_numerator / m_denominator; 
 	}
 	
 	Fraction operator+(const Fraction& f) {          // 运算符重载
	   cout << "operator+(): " << f.m_numerator << ' ' << f.m_denominator <<  endl;  
	   //... plus
	   return f; 
	} 
/*	
	Fraction(double d) 
	  : m_numerator(d * 1000), m_denominator(1000) 
	{ cout << m_numerator << ' ' << m_denominator << endl; }
*/

private:   
   int m_numerator;    // 分子
   int m_denominator;  // 分母
};
	
void test_conversion_functions()
{	
	cout << "test_conversion_functions()" << endl;
	
	Fraction f(3,5);
	
	double d = 4 + f;		//4.6
	cout << d << endl;
	
	//! Fraction d2 = f + 4;	 //ambiguous		
}

12. 智能指针类

  • 能比指针有更多的操作。

12.1 智能指针类头文件

template<class T>
class shared_ptr
{
public:
	T& operator*() const { return *px; }
	T* operator->() const { return px; }
	shared_ptr(T* p) : px(p) { }
private:
	T* px;
	long* pn;
}

struct Foo
{
	...
	void method(void);
	...
}

int main()
{
shared_ptr<Foo> sp(new Foo);
Foo f(*sp);                       // 取得指针所指的对象
sp->method();                     //相当于px->method()

}

12.2 迭代器类头文件

// 链表迭代器智能指针重点代码
T& operator*() const { return (*node).data; }
T* operator->() const { return &(operator*()); }

13. 函数类

  • 仿函数一般要继承一些奇怪的基类。
  • 含有重载()的就是仿函数。

13.1 读取字符串仿函数类

class GetInput
{
public:
	GetInput(std::istream &i = std::cin) : is(i) {}     // 带有默认值的构造函数
	std::string operator()() const                 
	{
		std::string str;
		std::getline(is, str);
		return is ? str : std::string();
	}

private:
	std::istream &is;    // 输入输出流必须是引用
};

// 取到每一行将它存入vector中
void test()
{
	GetInput getInput;
	std::vector<std::string> vec;
	for (std::string tmp; !(tmp = getInput()).empty();) vec.push_back(tmp);   // 这里是调用了对象里的函数
	for (const auto &str : vec) std::cout << str << " ";
	std::cout << std::endl;
}

13.2 判断两数是否相等仿函数类

class IsEqual
{
	int value;
public:
	IsEqual(int v) : value(v) {}
	bool operator()(int elem)
	{
		return elem == value;
	}
};

void test()
{
	std::vector<int> vec = { 3, 2, 1, 4, 3, 7, 8, 6 };
	std::replace_if(vec.begin(), vec.end(), IsEqual(3), 5);            // 这里是创建了一个对象
	for (int i : vec) std::cout << i << " ";
	std::cout << std::endl;
}

14. 模板类

  • 类模板在使用的时候要指明模板类型。

14.1 复数类

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

void test()
{
	complex<double> c1(2.5, 1.5);
	complex<int> c2(2, 6);
}

15. 函数模板

  • 函数模板在使用的时候不必指明模板类型,编译器自动进行实参推导。
template<class T>
inline
const T& min(const T& a, const T& b)
{
	return b < a ? b : a;
}

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

void test()
{
	stone r1(2, 3), r2(3, 3), r3;
	r3 = min(r1, r2);
}

16. 成员模板

  • 一般用在构造函数中(为了让类更加有弹性)。
template <class T1, class T2>
class pair
{
public:
	pair() : first(T1()), second(T2()) {}
	pair(const T1& a, const T2& b) :
		first(a), second(b) {}
	
	template<class U1, class U2>
	pair(const pair<U1, U2>& p) :
		first(p.first), second(p.second) {}
}

class Base1 {};
class Derived1 : pubic Base1 {};

class Base2 {};
class Derived2 : public Base2 {};

17. 模板特化

17.1 模板特化

template <class key>
struct hash {};

template<>
struct hash<char> {
	size_t operator()(char x) const { return x; }
};

template<>
struct hash<int> {
	size_t operator()(int x) const { return x; }
};

template<>
struct hash<char> {
	size_t operator()(long x) const { return x; }
};


void test()
{
	std::cout << hash<long>()(1000);
}

17.2 模板偏特化

17.2.1 个数的偏

template<typename T, typename Alloc=...>
class vector
{
...
};

template<typename Alloc=...>
class vector<bool, Alloc>
{
...
};

17.2.2 范围的偏

template<typename T>
class C
{
...
};

template<typename T>
class C<T*>
{
...
}

/* 可以换成U
template<typename U>
class C<U*>
{
...
}
*/

void test()
{
	C<string> obj1;
	C<string*> obj2;
}

18. 模板模板参数

18.1 模板模板参数实例

template <typename T,
		 template <typename T>
		 	class SmartPtr
		 >
class XCls
{
private:
	SmartPtr<T> sp;
public:
	XCls() : sp(new T) {}
}

void test()
{
	//template<typename T>
	//using Lst = List<T, allocator<T>>;
	//XCls<string, Lst> mylst2;
	XCls<string, shared_ptr> p1;
	XCls<long, auto_ptr> p2;
}

18.2 非模板模板参数

template <class T, class Sequence = deque<T>>
class stack
{
...
protected"
	Sequence x;  // 底层容器
}

void test()
{
	stack<int> s1;
	stack<int, list<int>> s2;
}

19. 数量不定的模板参数

void print() { }
template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
{
	cout << firstArg << endl;
	print(args...);
}

20. 对象模型

在这里插入图片描述
在这里插入图片描述

21. 重载new、delete

21.1 全局new、delete重载

void* myAlloc(size_t size)
{
	return malloc(size);
}
void myFree(void* ptr)
{
	return free(ptr);
}

inline 
void* operator new(size_t size)
{
	cout << "jjhou global new()  \n";
	return myAlloc(size);
}
inline 
void* operator new[](size_t size)
{
	cout << "jjhou global new[]()  \n";
	return myAlloc(size);
}
inline 
void operator delete(void* ptr)
{
	cout << "jjhou global delete()  \n";
	myFree(ptr);
}
inline 
void operator delete[](void* ptr)
{
	cout << "jjhou global delete[]()  \n";
	myFree(ptr);
}

21.2 类中new、delete重载

class Foo
{
public:
	void* operator new(size_t);
	void operator delete(void*, size_t);
}


void test()
{
	Foo* p = new Foo;
	...
	delete p;
}

try 
{
	void *mem = operator new(sizeof(Foo));
	p = static_cast<Foo*>(mem);
	p->Foo::Foo();
}

p->~Foo();
operator delete(p);

21.3 类中new[]、delete[]重载

class Foo
{
public:
	void* operator new[](size_t);
	void operator delete[](void*, size_t);
};


void test()
{
	Foo* p = new Foo [N];
	...
	delete [] p;
}


try 
{
	void *mem = operator new(sizeof(Foo) * N + 4);
	p = static_cast<Foo*>(mem);
	p->Foo::Foo();    // N次(这里数组有几个元素则调用几次构造函数)
}

p->~Foo();     // N次(这里数组有几个元素则调用几次析构函数)
operator delete(p);

21.4 类中new、delete重载完整代码

class Foo
{
public:
	int _id;
	long _data;
	string _str;

public:
	Foo() : _id(0) { cout << "default ctor.this=" << this << "id=" << _id << endl; }
	Foo(int i) : _id(i) { cout << "default ctor.this=" << this << "id=" << _id << endl; }	
	~Foo() { cout << "dtor.this=" << this << "id=" << _id << endl;}
	void* operator new(size_t size) {
		Foo* p = (Foo*)malloc(size);
		cout << ...
		return p;
	}
	void* operator new[](size_t size) {
		Foo* p = (Foo*)malloc(size);
		cout << ...
		return p;
	}
	void operator delete(void* pdead, size_t size) {
		cout << ...
		free(pdead);
	}
	void operator delete[](void* pdead, size_t size) {
		cout << ...
		free(pdead);
	}
};

在这里插入图片描述

21.4 类中new()、delete()重载

class Foo
{
public:
	Foo() { cout << "Foo::Foo()" << endl; }
	Foo(int) { cout << "Foo::Foo(int)" << endl;		throw Bad(); }	
	~Foo() { cout << "dtor.this=" << this << "id=" << _id << endl;}

	// 这是一般的operator重载
	void* operator new(size_t size) {
		return malloc(size);
	}
	// 这是标准库已经提供的placement new()的重载
	void* operator new(size_t size, void* start) {
		return start;
	}
	// 这是崭新的placement new
	void* operator new(size_t size, long extra) {
		return malloc(size + extra);
	}
	// 这是又一个placement new
	void* operator new(size_t size, long extra, char init) {
		return malloc(size + extra);
	}


	void operator delete(void*, size_t) {
		cout << "operator delete(void*, size_t)" << endl;
	}	
	void operator delete(void*, void*) {
		cout << "operator delete(void*, void*)" << endl;
	}
	void operator delete(void*, long) {
		cout << "operator delete(void*, long)" << endl;
	}
	void operator delete(void*, long, char) {
		cout << "operator delete(void*, long, char)" << endl;
	}
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值