C++概览-------抽象机制2018-6-26

:具体类型;抽象类型;虚函数;类层次

拷贝移动:拷贝容器;资源管理;抑制制作

模板:参数化类型;函数模板;函数对象;可变参数模板;别名

C++如何支持抽象资源管理。支持面向对象编程泛型编程等编程风格。


C++最核心的语言特性就是。类是一种用户自定义数据类型

具体类别

一种算术类型

复数类complex----------标准库中是有的

定义在类内部函数默认是内联的。

无需实参的构造函数称为 默认构造函数,定义该类型函数可以 防止该类型的对象未初始化

complex.h

class complex{
private:
	double re, im;   //表现形式,两个双精度浮点数
public:
	complex(double r, double i) :re{ r }, im{ i }{}
	complex(double r) :re{ r }{}
	complex() :re{ 0 }, im{ 0 }{}


	double real() const{ return re; }   //const说明不会修改对象
	void real(double d){ re = d; }
	double imag() const{ return im; }
	void imag(double d){ im = d; }
	
	complex& operator+=(complex z){ re += z.re, im += z.im; return *this; }  //返回的是引用
	complex& operator-=(complex z){ re -= z.re, im -= z.im; return *this; }
	complex& operator*=(complex z){
		double temp1 = re;
		double temp2 = im;
		re = (temp1*z.re - temp2*z.im), 
		im = (temp1*z.im + temp2*z.re);    
		return *this;
	}


	//complex operator+(complex a, complex b){ return a += b; }    ////双目运算符重载声明在类外,否则就会出错
};


complex operator+(complex, complex);
complex operator-(complex, complex);
complex operator-(complex);
complex operator*(complex, complex);
bool operator==(complex, complex);
bool operator!=(complex, complex);

complex.cpp

#include "complex.h"

complex operator+(complex a, complex b){ return a += b; }
complex operator-(complex a, complex b){ return a -= b; }
complex operator-(complex a){ return{ -a.real(), -a.imag() }; }
complex operator*(complex a, complex b){ return a *= b; }
bool operator==(complex a, complex b){ 
	return a.real() == b.real() && a.imag() == b.imag();
}
bool operator!=(complex a, complex b){
	return !(a == b);
}

test.cpp

#include <iostream>
#include "complex.h"
using namespace std;

void f(){
	complex a(2,1);
	complex b(2,3);
	complex c(a * b);
	cout << c.real() << "  " <<c.imag() << '\n';
}

int main(){
	f();
}


容器

容器是指一个包含若干元素对象

Vector为容器类型,Vector的对象都是容器,Vector作为double的容器

更精确的资源释放控制----析构函数

~Vector(){delete[] elem;}


初始化容器

1.初始化器列表构造函数

2.push_back(): 在序列的末尾添加一个新元素


抽象类型

抽象类型使用者与类的实现细节完全隔离开来。抽象类型分离接口表现形式并且放弃了纯局部变量

含有纯虚函数的类称为抽象类

针对Vector_container类

#include <iostream>
using namespace std;

class Vector{   //具体类
public:
	Vector(int s) :elem{ new double[s] }, sz{ s }{} //初始化  使用了成员初始化器列表来初始化成员  
	double& operator[](int i){ return elem[i]; }  //通过下标访问  
	int size() const{ return sz; }
private:
	double* elem;   //指向元素的指针  
	int sz;    //元素的个数  
};

class Container{           //比Vector更抽象的类,称为抽象类,= 0为纯虚函数,派生类必须定义这个函数
public:
	virtual double& operator[](int) = 0;   //纯虚函数
	virtual int size() const = 0;
	virtual ~Container(){}          //析构函数
};
//作为一个抽象类,在 Container中没有构造函数,毕竟它没有什么数据需要初始化,析构函数也是虚的,抽象类需要通过引用或指针来操纵,但不清楚它的实现部分到底拥有哪些资源

void use(Container& c){     //use()在完全忽视实现细节的情况下使用了Container的接口 size() [],却根本不知道哪个类型实现了它们
	const int sz = c.size();   //如果一个类负责为其他一些类提供接口,将前者称为“多态类型”

	for (int i = 0; i != sz; ++i){
		cout << c[i] << '\t';
	}
}

//一个容器为了实现抽象类Container接口所需的函数,可以使用具体类Vector,派生类
//基类  子类和超类  继承
class Vector_container : public Container{    //Vector_container实现了Container
	Vector v;  //使用了具体类型Vector     
public:
	Vector_container(int s) :v(s){}  //初始化,含有s个元素的Vector  注意成员v的构造函数被隐式调用
	~Vector_container(){};             

	double& operator[](int i){ return v[i]; } //覆盖 override
	int size() const { return v.size(); }  //覆盖  override
};

void g(){
	Vector_container vc(5);
	for (int i = 0; i != vc.size(); ++i)
		cin >> vc[i];
	use(vc);   //因为use()只知道Container的接口而不了解Vector_container,因此对于Container的其他实现,use()仍能正常工作
}

int main(){
	g();
}


下面是针对List_container类

class List_container : public Container{   //List_container实现了Container
	std::list<double> ld;   //一个double类型的标准库list  需要在前面#include <list>
public:
	List_container(){}
	List_container(initializer_list<double> il) :ld{il}{}
	~List_container(){}
	double& operator[](int i);
	int size() const { return ld.size(); }
};

double& List_container::operator[](int i){
	for (auto& x : ld){   //注意x是从头遍历的,所以通过i==0 和 --i配合,可以定位到具体的i位置
		if (i == 0) return x;  
		--i;
	}
	throw out_of_range("List Container");
}

void h(){
	List_container lc = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	use(lc);
}


虚函数

虚函数表 virtual function table, vtbl


类层次

类层次是指通过 派生创建的一组类。

接口继承  、  实现继承。

具体类:类似于内置类型,通常将其定义为局部变量

类层次中的类,倾向于通过new自由存储中为其分配空间,然后通过指针引用访问他们。

释放内存很重要  delete  unique_ptr


拷贝和移动

逐成员的复制: complex 

但是对于Vector一样的复杂具体类型,逐成员的复制常常是不正确的,抽象类型更是如此。

拷贝容器

拷贝构造函数  和  拷贝赋值运算符

#include <iostream>
using namespace std;

class Vector{
private:
	double* elem;  //指向拥有sz个double的数组
	int sz;
public:
	Vector(int s) :elem{ new double[s] }, sz{ s }{};
	~Vector(){ delete[] elem; }

	Vector(const Vector& a);    //拷贝构造函数
	Vector& operator=(const Vector& a);  //拷贝赋值运算符

	double& operator[](int i){ return elem[i]; }
	const double& operator[](int i)const{ return elem[i]; };    //函数末尾加const表示该函数不修改类中的成员变量

	int size()const{ return sz; }

};

Vector::Vector(const Vector& a) :elem{ new double[a.sz] }, sz{ a.sz }{
	for (int i = 0; i != sz; ++i)
		elem[i] = a.elem[i];
}

Vector& Vector::operator=(const Vector& a){
	double* p = new double[a.sz];
	for (int i = 0; i != a.sz; ++i){
		p[i] = a.elem[i];
	}
	delete[] elem; //删除旧元素
	elem = p;
	sz = a.sz;
	return *this;
}


int main(){
	Vector v1(5);
	for (int i = 0; i != v1.size(); ++i){
		cin >> v1[i];
	}
	Vector v2 = v1;
	v2[0] = 100;
	for (int i = 0; i != v2.size(); ++i){
		cout << v2[i]<<'\t';
	}
	cout << '\n';
	for (int i = 0; i != v2.size(); ++i){
		cout << v1[i] << '\t';
	}
}


移动容器

移动构造函数 Vector(Vector&& a)

移动赋值运算符Vector& operator=(Vector&& a)

标准库函数 move高效率的使用内存左值   右值


资源管理

通过定义 构造函数 拷贝操作 移动操作 析构函数 程序员就可以对受控资源(比如容器中的元素)全生命周期进行管理。

在很多情况下, 使用资源句柄比用指针效果更好,容易实现 强资源安全(strong resource safety),消除 资源泄露


抑制操作

=delete


3.4 模板

一个 模板(template)就是一个类或函数,需要一组类型或值对其 进行参数化,使用模板表示 通用的概念,通过指定实参生成 特定类型的类或函数


参数化类型

#include <iostream>
#include <string>
using namespace std;

template<typename T>      //前缀template<typename T>指明T是该声明的形参
class Vector{
private:
	T* elem;  //elem指向含有sz个T类型元素的数组
	int sz;
public:
	Vector(int s);  //构造函数:建立不变式,获取资源 
	~Vector(){ delete[] elem; }   //析构函数,释放资源

	T& operator[](int i);
	const T& operator[](int i) const;
	int size(){ return sz; }

};

template<typename T>
Vector<T>::Vector(int s){
	//if (s < 0) throw Negative_size{};
	elem = new T[s];
	sz = s;
}

//template<typename T>
//const T& Vector<T>::operator[](int i) const{
//	if (i < 0 || size() < i)
//		throw out_of_range{"Vector::operator[]"};
//	return elem[i];
//}

template<typename T>
T& Vector<T>::operator[](int i){
	if (i < 0 || size() < i)
		throw out_of_range{"Vector::operator[]"};
	return elem[i];
}


int main(){
	Vector<string> v1(5);
	for (int i = 0; i != v1.size(); ++i){
		cin >> v1[i];
	}
	for (int i = 0; i != v1.size(); ++i){
		cout << v1[i]<<'\t';
	}

}



函数模板

模板能 参数化标准库中的很多 类型算法
#include <iostream>
#include <list>
#include <vector>
using namespace std;

template<typename Container, typename Value>
Value sum(const Container& c, Value v){
	for (auto x : c){
		v += x;
	}
	return v;
}

void user(){
	list<double> d1 = { 1.0, 2.2, 3.3, 4.4, 5.5 };
	vector<int> d2 = { 1, 3, 3, 3 };
	double sum1 = sum(d1, 0.0);
	int sum2 = sum(d2, 0);
	cout << "sum1 = " << sum1 << '\n';
	cout << "sum2 = " << sum2 << '\n';
}

int main(){
	user();
}

这里的sum()可以看做是标准库accumulate()的简化版本


函数对象

#include <iostream>
#include <list>
#include <vector>
using namespace std;

//函数对象,是对象就会有类
template<typename T>
class Less_than{
	const T val;  //常量
public:
	Less_than(const T& v) :val(v){};   //参数初始化对象
	bool operator()(const T& x)const{ return x < val; } //运算符,函数对象的主要点在此!
};

int main(){
	//为某些实参类型定义Less_than类型的具体对象
	Less_than<int> lti{ 42 };
	Less_than<double> ltd{ 2.2 };
	//接下来,像调用函数一样调用该对象
	bool b1 = lti(21);
	bool b2 = ltd(1.0);
	cout << b1 << '\t' << b2 << '\n';
}

#include <iostream>
#include <list>
#include <vector>
using namespace std;


//函数对象,是对象就会有类
template<typename T>
class Less_than{
	const T val;  //常量
public:
	Less_than(const T& v) :val(v){};   //参数初始化对象
	bool operator()(const T& x)const{ return x < val; } //运算符,函数对象的主要点在此!
};


//int main(){
//	//为某些实参类型定义Less_than类型的具体对象
//	Less_than<int> lti{ 42 };
//	Less_than<double> ltd{ 2.2 };
//	//接下来,像调用函数一样调用该对象
//	bool b1 = lti(21);
//	bool b2 = ltd(1.0);
//	cout << b1 << '\t' << b2 << '\n';
//}


template<typename C, typename P>
int count(const C& c, P pred){     //pred就是函数对象,作为函数实参
	int cnt = 0;
	for (const auto&x : c){
		if (pred(x))
			++cnt;
	}
	return cnt;
}


int main(){
	list<double> d1 = { 1.0, 2.2, 3.3, 4.4, 5.5 };
	Less_than<double> ltd{ 6.6 };    //作为函数的实际参数


	cout << count(d1, ltd) << "\n";
}










体会:类+函数, main()函数中代码越少越好,精心地设计类和函数

Less_than中简单函数内联效率更高

函数对象的精妙之处在于它们随身携带着准备与之比较的值


lambda expression, lambda 表达式隐式生成函数对象的表示法

[&](int a){return a<x;}


可变参数模板

variadic template  接受 任意数量任意类型的实参。


别名

using size_t =  unsigned int;








猜你喜欢

转载自blog.csdn.net/qq_28088259/article/details/80818559