C++数据抽象——对象与类详解

数据抽象——对象与类

C++对象与类自学笔记,以课本为基础,加了一点自己的改进,并附有相关代码。发布在博客上作为记录,并且勉励自己!

1.用类定义栈

#include<iostream>
using namespace std;
class MyStack
{
public:
	MyStack()
	{ 
		mytop = NULL;
		len = 0;
	}//构造函数
	void push(int i);
	void pop();
	int top();
	bool isempty();
	int size();
	void swap(MyStack &target);
private:
	struct Node
	{
		int content;
		Node* next;
	}*mytop;

	int len;
};
void MyStack::push(int i)
{
	Node* p = new Node;
	if (p == NULL)
	{
		cerr << "Stack is empty.\n";
		exit(-1);
	}
	else
	{
		p->content = i;
		p->next = mytop;
		mytop = p;
		this->len++;
		return;
	}
}
void MyStack::pop()
{
	if (mytop == NULL)
	{
		cerr << "Stack is empty.\n";
		exit(-1);
	}
	else
	{
		Node* p = mytop;
		mytop = mytop->next;
		delete p;
		this->len--;
		return;
	}
}
int MyStack::top()
{
	if (this->mytop == NULL)
	{
		cerr << "Stack is empty.\n";
		exit(-1);
	}
	else
		return this->mytop->content;
}
bool MyStack::isempty()
{
	if (this->mytop == NULL)
		return 1;
	else
		return 0;
}
int MyStack::size()
{
	return this->len;
}

void MyStack::swap(MyStack& target)
{
	MyStack st1, st2;
	while (!this->isempty())
	{
		st1.push(this->mytop->content);
		this->pop();
	}
	while (!target.isempty())
	{
		st2.push(target.mytop->content);
		target.pop();
	}

	while (!st2.isempty())
	{
		this->push(st2.mytop->content);
		st2.pop();
	}
	while (!st1.isempty())
	{
		target.push(st1.mytop->content);
		st1.pop();
	}

	return;
}
int main()
{
	//调试代码
	MyStack st1, st2;
	if (st1.isempty())
		cout << "st1 is empty." << endl;
	if (st2.isempty())
		cout << "st2 is empty." << endl;
	for (int i = 0; i < 4; i++)
		st1.push(i);
	for (int i = 4; i < 8; i++)
		st2.push(i);
	cout << "Size of st1 is:" << st1.size() << endl;
	cout << "Size of st2 is:" << st2.size() << endl;
	
	cout << "Before swaping, the top of st1 is " << st1.top() << endl;
	cout << "Before swaping, the top of st2 is " << st2.top() << endl;

	st1.swap(st2);
	cout << "After swaping, the top of st1 is " << st1.top() << endl;
	cout << "After swaping, the top of st2 is " << st2.top() << endl;
	
}

2.构造函数

#include<iostream>
using namespace std;
class A
{
public:
	A() { cout << "默认构造函数\n"; };//无参数或者参数全为默认值
	A(int i){cout << "参数为int的构造函数\n"; }
	A(char *p) { cout << "参数为char *p的构造函数\n"; }
};

int main()
{
	A a1;//可以写成A a1=A();
	A a2(1);//可以写成A a2=A(1);或A a2=1;
	char p[]="abcd";
	A a3(p);//不知为何不能写成 A a3("abcd");
	A a[4];//调用对象a[0]~a[3]的默认构造函数
	A b[5] = { A(),A(1),A(p),2,p };
}

3.成员初始化表

#include<iostream>
using namespace std;
class A
{
	int x;
	const int y;
	int& z;
public:
	A() :z(x), y(1)
	{
		x = 0;
	}
	//or
	/*
	A() :x(0),z(x),y(1)
	{
	}
	*/
public:
	void Print()
	{
		cout << "x初始化的值为" << this->x << endl;
		cout << "z初始化指向x,z指向的值为" << this->z << endl;
		cout << "常量y的值为" << this->y << endl;
	}
};
int main()
{
	A a;
	a.Print();
}

4.析构函数

//名称:~A()
//基于数据抽象与封装思想定义一个类来描述字符串
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
class String
{
private:
	char* str;
public:
	String()
	{
		str = NULL;
	}
	String(const char* p)
	{
		str = new char[strlen(p) + 1];
		strcpy(str, p);
	}
	~String()
	{
		delete []str;
		str = NULL;
	}
	String(const String& s)//显式定义拷贝构造函数
	{
		str = new char[strlen(s.str) + 1];
		strcpy(str, s.str);
	}
	int length() { return strlen(str); }
	char& char_at(int i)
	{
		if (i < 0 || i >= strlen(str))
		{
			cerr << "超出字符串范围!\n";
			exit(-1);
		}
		return str[i];
	} 
	const char* get_str()//返回字符串指针
	{
		return str;
	}
	//下面两个重载函数实现字符串复制功能
	String& copy(const char* p)
	{
		delete[]str;//归还原来的资源
		str = new char[strlen(p) + 1];//申请新资源
		strcpy(str, p);
		return *this;
	}
	String& copy(const String& s) { return copy(s.str); }
	//下面两个重载函数实现字符串拼接功能
	String& append(const char* p)
	{
		char* p1 = new char[strlen(str) + strlen(p) + 1];
		strcpy(p1, str);
		strcat(p1, p);
		delete[]str;
		str = p1;
		return*this;
	}
	String& append(const String& s) { return append(s.str); }

	//下面两个重载函数实现字符串比较功能
	int compare(const char* p) { return strcmp(str, p); }
	int compare(const String& s) { return strcmp(str, s.str); }

};
int main()
{
	String s1;
	String s2("abcdefg");
	s1.copy("xyz");
	s2.append(s1);
	for (int i = 0; i < s2.length(); i++)//把s2中的小写字母变成大写字母
	{
		if (s2.char_at(i) >= 'a' && s2.char_at(i) <= 'z')
			s2.char_at(i) = s2.char_at(i) - 'a' + 'A';
	}
	if (s2.compare("ABCDEFGXYZ") == 0)cout << "OK\n";
	cout << s1.get_str() << endl << s2.get_str() << endl;
	return 0;
}

5.成员对象初始化

#include<iostream>
using namespace std;
class A
{
	int m;
public:
	A() { m = 0; }
	A(int m1) { m = m1; }
	int get_m()
	{
		return m;
	}
};
class B
{
	int n;
	A a;
public:
	B() { n = 0; }
	B(int n1) { n = n1; }
	B(int n1, int m1) :a(m1) { n = n1; }
public:
	int get_n()
	{
		return this->n;
	}
	int get_am()
	{
		return this->a.get_m();
	}
};
int main()
{
	B b1, b2(1);
	B b3(1, 2);
	cout << "b1的n值为:" << b1.get_n() << endl;
	cout << "b1的a的m值为:" << b1.get_am() << endl;
	cout << "b2的n值为:" << b2.get_n() << endl;
	cout << "b2的a的m值为:" << b2.get_am() << endl;
	cout << "b3的n值为:" << b3.get_n() << endl;
	cout << "b3的a的m值为:" << b3.get_am() << endl;
}

6.拷贝构造函数

(1).一般用法

#include<iostream>
using namespace std;
class A
{
	int m;
public:
	A() { m = 0; }//默认构造函数
	A(int i) { m = i; }
	A(const A& a)//拷贝构造函数,若不声明拷贝构造函数,则系统会隐式地给出
	{
		this->m = a.m;
	}
public:
	int get_m()
	{
		return m;
	}
};
int main()
{
	A a1(1);
	A a2(a1);
	cout << a2.get_m() << endl;
}

(2).显示拷贝构造函数

#include<iostream>
using namespace std;
class A
{
	int x, y;
	char* p;
public:
	A(char* str)
	{
		x = 0;
		y = 0;
		p = new char[strlen(str) + 1];
		strcpy(p, str);
	}
	~A() { delete[]p; p = NULL; }
    A(const A& a);
public:
	char * get_p()
	{
		return p;
	}
};
A::A(const A& a)
{
	x = a.x;
	y = a.y;
	p = new char[strlen(a.p) + 1];
	strcpy(p, a.p);
}
int main()
{
	char p[] = "abcd";
	A a1(p);
	A a2(a1); //ERROR:如果使用系统提供的隐式拷贝构造函数,用a1对a2进行初始化,那么会产生以下问题:
	/*
	1.如果对对象a1操作之后修改了这块空间的内容,则对象a2将会受到影响。
	2.当对象a1和a2消亡时,将会分别去调用它们的析构函数,这会使得同一块内存区域被归还两次,从而导致程序异常
	3.当对象a1和a2中有一个消亡,另一个还没消亡时,会出现使用已归还的空间错误。
	*/
	//解决方案:定义一个显示拷贝构造函数
	
	cout << a1.get_p() << endl;
	cout << a2.get_p() << endl;
}

(3).类中包含成员对象时的拷贝构造函数

/*当类定义中有成员对象时,系统提供的隐式拷贝构造函数会调用成员对象的拷贝构造函数,但自定义的拷贝构造函数
* 则会默认调用成员对象的默认构造函数,而不是调用成员对象的拷贝构造函数。
*/
#include<iostream>
using namespace std;
class A
{
	int x, y;
public:
	A() { x = y = 0; }
	void inc() { x++, y++; }
	int get_x() { return x; }
	int get_y() { return y; }
};
class B
{
	int z;
	A a;
public:
	B() { z = 0; }
	B(const B& b) { z = b.z; }
	void inc() { z++; a.inc(); }
	int get_z() { return z; }
	int get_ax() { return a.get_x(); }
	int get_ay() { return a.get_y(); }
};
int main()
{
	B b1;
	b1.inc();
	B b2(b1);
	cout << "b1的z值为:" << b1.get_z() << endl;
	cout << "b1的a的x值为:" << b1.get_ax() << endl;
	cout << "b1的a的y值为:" << b1.get_ay() << endl<<endl;
	cout << "b2的z值为:" << b2.get_z() << endl;
	cout << "b2的a的x值为:" << b2.get_ax() << endl;
	cout << "b2的a的y值为:" << b2.get_ay() << endl;
	//解决方法,显示定义拷贝构造函数
	//B(const B&b):a(b.a){z=b.z;}
}

7.const成员函数

#include<iostream>
using namespace std;
class A
{
	int x;
	char* p;
public:
	void f()const//不改变类中参数的值,函数括号后加“const”
	{
		//x = 1; //ERROR,会报错
		*p = 'A';//不会报错,因为没有改变p的值,需要自己把握
	}
};
class B
{
	int x, y;
public:
	void f()const
	{
		cout << "This is Func f" << endl;
	}
	void g()
	{
		cout << "This is Func g" << endl;
	}
};
int main()
{
	//还可以检验常量对象
	const B b;
	b.f();//OK
	//b.g();  //ERROR
}

8.静态数据成员

#include<iostream>
using namespace std;
class A
{
	int x, y;
	static int shared;
public:
	A() { x = y = 0; }
	void increase_all() { x++; y++; shared++; }
	int sum_all()const { return x + y + shared; }
	int get_z()const { return shared; }
};
int A::shared = 0;
class B
{
	int x, y;
	static int z;
public:
	B() { x = y = 0; }
	static void set_z(int i) { z = i; }//静态成员函数,只能对静态变量进行操作,且没有隐藏的this指针
	static int get_z() { return z; }
};
int B::z;
int main()
{

	A a1,a2;
	cout << "一开始a2中静态变量shared的值为:" << a2.get_z() << endl << endl;
	a1.increase_all();
	cout << "由于所有对象的静态变量shared都只存在一个拷贝,改编a1中shared的值,a2中shared的值也会跟着改编,故自增后shared的值为:" << a2.get_z() << endl;
	
	B b1;
	//访问静态成员函数的两种方式:
	//1.通过对象进行访问
	b1.set_z(10);
	//2.通过类名受限访问
	cout << "b1设置的z值为:" << B::get_z() << endl;
}

9.友元

(1).不使用友元实现矩阵和向量乘法

//友元,可以提高访问效率
//友元不具有传递性
//例:用类来实现矩阵和向量类型并实现矩阵向量相乘操作
#include<iostream>
using namespace std;
class Matrix
{
	int* p_data;
	int row, col;
public:
	Matrix(int r, int c)
	{
		if (r <= 0 || c <= 0)
		{
			cerr << "矩阵尺寸不合法!" << endl;
			exit(-1);
		}
		row = r, col = c;
		p_data = new int[row * col];
	}
	~Matrix()
	{
		delete[]p_data;
	}
	int& element(int i, int j)//访问矩阵元素
	{
		if (i < 0 || i >= row || j < 0 || j >= col)
		{
			cerr << "矩阵下标越界\n";
			exit(-1);
		}
		return*(p_data + i * col + j);
	}
	int element(int i, int j)const//访问矩阵元素(为常量对象提供)
	{
		if (i < 0 || i >= row || j < 0 || j >= col)
		{
			cerr << "矩阵下标越界\n";
			exit(-1);
		}
		return*(p_data + i * col + j);
	}
	int dimension_row()const
	{
		return row;
	}
	int dimension_col()const
	{
		return col;
	}
	void display()const//显示矩阵元素
	{
		int* p = p_data;
		for (int i = 0; i < row; i++)
		{
			for (int j = 0; j < col; j++)
			{
				cout << *p << ' ';
				p++;
			}
			cout << endl;
		}
	}
};
class Vector//向量类
{
	int* p_data;
	int num;
public:
	Vector(int n)
	{
		if (n <= 0)
		{
			cerr << "向量尺寸不合法!\n";
			exit(-1);
		}
		num = n;
		p_data = new int[num];
	}
	~Vector()
	{
		delete[]p_data;
	}
	int& element(int i)
	{
		if (i < 0 || i >= num)
		{
			cerr << "数组下标越界\n";
			exit(-1);
		}
		return p_data[i];
	}
	int element(int i)const
	{
		if (i < 0 || i >= num)
		{
			cerr << "数组下标越界\n";
			exit(-1);
		}
		return p_data[i];
	}
	int dimension()const
	{
		return num;
	}
	void display()const
	{
		int* p = p_data;
		for (int i = 0; i < num; i++, p++)
			cout << *p << ' ';
		cout << endl;
	}
};
void multiply(const Matrix& m, const Vector& v, Vector& r)
{
	if (m.dimension_col() != v.dimension() ||
		m.dimension_row() != r.dimension())
	{
		cerr << "矩阵和向量尺寸不匹配!" << endl;
		exit(-1);
	}
	int row = m.dimension_row();
	int col = m.dimension_col();
	for (int i = 0; i < row; i++)
	{
		r.element(i) = 0;
		for (int j = 0; j < col; j++)
			r.element(i) += m.element(i, j) * v.element(j);
	}
}
int main()
{
	int row, column;
	cout << "请输入矩阵的行数和列数:";
	cin >> row >> column;
	Matrix m(row, column);
	Vector v(column);
	Vector r(row);
	cout << "请输入矩阵元素:\n";
	int i, j;
	for (i = 0; i < row; i++)
		for (j = 0; j < column; j++)
			cin >> m.element(i, j);
	cout << "请输入向量元素:\n";
	for (int i = 0; i < column; i++)
		cin >> v.element(i);
	multiply(m, v, r);
	cout << "矩阵:\n";
	m.display();
	cout << "与向量:\n";
	v.display();
	cout << "的乘积为:\n";
	r.display();
	return 0;
	//上述函数multiply中多次调用成员函数element访问元素,每一次调用都要判断下标是否合法,效率不高
	//可以使用友元函数,在函数中直接取存它们的私有成员,访问效率大大提高
}

(2).使用友元实现(1)中功能

//接上友元
#include<iostream>
using namespace std;
class Vector;//先声明,以后会用到
class Matrix
{
	int* p_data;
	int row, col;
public:
	Matrix(int r, int c)
	{
		if (r <= 0 || c <= 0)
		{
			cerr << "矩阵尺寸不合法!" << endl;
			exit(-1);
		}
		row = r, col = c;
		p_data = new int[row * col];
	}
	~Matrix()
	{
		delete[]p_data;
	}
	int& element(int i, int j)//访问矩阵元素
	{
		if (i < 0 || i >= row || j < 0 || j >= col)
		{
			cerr << "矩阵下标越界\n";
			exit(-1);
		}
		return*(p_data + i * col + j);
	}
	int element(int i, int j)const//访问矩阵元素(为常量对象提供)
	{
		if (i < 0 || i >= row || j < 0 || j >= col)
		{
			cerr << "矩阵下标越界\n";
			exit(-1);
		}
		return*(p_data + i * col + j);
	}
	int dimension_row()const
	{
		return row;
	}
	int dimension_col()const
	{
		return col;
	}
	void display()const//显示矩阵元素
	{
		int* p = p_data;
		for (int i = 0; i < row; i++)
		{
			for (int j = 0; j < col; j++)
			{
				cout << *p << ' ';
				p++;
			}
			cout << endl;
		}
	}
	friend void multiply(const Matrix& m, const Vector& v, Vector& r);
};
class Vector//向量类
{
	int* p_data;
	int num;
public:
	Vector(int n)
	{
		if (n <= 0)
		{
			cerr << "向量尺寸不合法!\n";
			exit(-1);
		}
		num = n;
		p_data = new int[num];
	}
	~Vector()
	{
		delete[]p_data;
	}
	int& element(int i)
	{
		if (i < 0 || i >= num)
		{
			cerr << "数组下标越界\n";
			exit(-1);
		}
		return p_data[i];
	}
	int element(int i)const
	{
		if (i < 0 || i >= num)
		{
			cerr << "数组下标越界\n";
			exit(-1);
		}
		return p_data[i];
	}
	int dimension()const
	{
		return num;
	}
	void display()const
	{
		int* p = p_data;
		for (int i = 0; i < num; i++, p++)
			cout << *p << ' ';
		cout << endl;
	}
	friend void multiply(const Matrix& m, const Vector& v, Vector& r);
};
void multiply(const Matrix& m, const Vector& v, Vector& r)
{
	if (m.col!= v.num ||
		m.row != r.num)
	{
		cerr << "矩阵和向量尺寸不匹配!" << endl;
		exit(-1);
	}
	int* p_m = m.p_data, * p_r = r.p_data, * p_v;
	for (int i = 0; i < m.row; i++)
	{
		*p_r = 0;
		p_v = v.p_data;
		for (int j = 0; j < m.col; j++)
		{
			*p_r += (*p_m) * (*p_v);
			p_m++;
			p_v++;
		}
		p_r++;
	}
}
int main()
{
	int row, column;
	cout << "请输入矩阵的行数和列数:";
	cin >> row >> column;
	Matrix m(row, column);
	Vector v(column);
	Vector r(row);
	cout << "请输入矩阵元素:\n";
	int i, j;
	for (i = 0; i < row; i++)
		for (j = 0; j < column; j++)
			cin >> m.element(i, j);
	cout << "请输入向量元素:\n";
	for (int i = 0; i < column; i++)
		cin >> v.element(i);
	multiply(m, v, r);
	cout << "矩阵:\n";
	m.display();
	cout << "与向量:\n";
	v.display();
	cout << "的乘积为:\n";
	r.display();
	return 0;
	//上述函数multiply中多次调用成员函数element访问元素,每一次调用都要判断下标是否合法,效率不高
	//可以使用友元函数,在函数中直接取存它们的私有成员,访问效率大大提高
}

10.构造转移函数

//对象拷贝构造的优化——构造转移函数
/*当用一个即将消亡的对象去初始化另一个同类对象时,有时目前的拷贝构造函数实现效率并不高,例如
* ,对于下面的程序:
*/
#include<iostream>
using namespace std;
class A
{
	char* p;
public:
	A(char* str) { p = new char[strlen(str) + 1]; strcpy(p, str); }
	A(const A& x)
	{
		p = new char[strlen(x.p) + 1]; strcpy(p, x.p);
	}
	~A()
	{
		delete[]p;
		p = NULL;
	}
	//转移构造函数
	A(A&& x)
	{
		p = x.p;//把参数对象x的p所指向的空间作为新对象的p所指的空间(资源转移)
		x.p = NULL;//使得参数对象x的p不再拥有原来所指向的空间
	}
	friend int main();
};
A f()
{
	char p[] = "1234";
	A t(p);
	//......一系列操作
	return t;//如果不加转移构造函数,这时候需要:创建返回值对象,用即将消亡的对象t对其进行初始化(调用拷贝构造函数),对象t消亡
}
int main()
{
	A a = f();
	cout << a.p << endl;
	//要避免为返回值对象申请新空间、内容复制以及归还对象t申请的空间所带来的开销,可以使用转移构造函数

}

11.操作符重载

(1).一般双目操作符重载(+,-,*,/)和一般单目操作符重载

//操作符重载:
//c++中没有对类等专门定义操作符(+,-,*,/),可以通过操作符重载来实现
//形式:<函数类型> <operator> <某个操作符> (参数)

//也可以作为全局函数定义
//操作符重载的基本原则
//1.遵循已有操作符的语法
//2.遵循已有操作符的语义
//3.可重载的操作符:除下面五个操作符外其余皆可重载
//“.”(成员选择符) “.*”(间接成员选择符) “::”(域解析符)“?:”(条件操作符)“sizeof”(数据占内存大小)

//双目操作符重载:
//形式(作为成员函数):<返回值类型> operator <运算符> (<类型>)
//使用格式:<类名> a; <类名> b; a#b; (#为定义的运算符)
//或 a.operator#(b)

//单目操作符重载:
//形式:<返回值类型> operator <运算符> (<类型>)

//实现复数加减乘除,==,!=和-
class Complex
{
	double real, imag;
public:
	Complex(double r = 0.0, double i = 0.0) { real = r, imag = i; }
	void display()const
	{
		if (real != 0)
		{
			cout << real;
			if (imag > 0)
			{
				cout << '+' << imag << 'i';
				return;
			}
			else if (imag == 0)
				return;
			else if (imag < 0)
			{
				cout << imag << 'i';
				return;
			}
		}
		else
		{
			if (imag > 0)
			{
				cout << imag << 'i';
				return;
			}
			else if (imag == 0)
			{
				cout << 0;
				return;
			}
			else if (imag < 0)
			{
				cout << imag << 'i';
				return;
			}
		}
	}
public:
	//双目操作符重载
	Complex operator + (const Complex& x)const
	{
		Complex temp;
		temp.real = real + x.real;
		temp.imag = imag + x.imag;
		return temp;
	}
	Complex operator -(const Complex& x)const
	{
		Complex temp;
		temp.real = real - x.real;
		temp.imag = imag - x.imag;
		return temp;
	}
	Complex operator *(const Complex& x)const
	{
		Complex temp;
		temp.real = real * x.real - imag * x.imag;
		temp.imag = imag * x.real + real * x.imag;
		return temp;
	}
	Complex operator /(const Complex& x)const
	{
		Complex temp;
		Complex conj(x.real, -x.imag);
		temp = *this * conj;
		int mod_x = x.real * x.real + x.imag * x.imag;
		temp.real = temp.real / mod_x;
		temp.imag = temp.imag / mod_x;
		return temp;
	}
	bool operator ==(const Complex& x)const
	{
		return (real == x.real) && (imag == x.imag);
	}
	bool operator !=(const Complex& x)const
	{
		return !(*this == x);
	}
	//单目操作符重载
	Complex operator -() const
	{
		Complex temp;
		temp.real = -real;
		temp.imag = -imag;
		return temp;
	}
};
int main()
{
	Complex c1(1, 3);
	Complex c2(1, -3);
	Complex c3 = c1 + c2;
	Complex c4 = c1 - c2;
	Complex c5 = c1 * c2;
	Complex c6 = c1 / c2;
	cout << "复数";
	c1.display();
	cout << "与复数";
	c2.display();
	cout << "的和为:";
	c3.display();
	cout << endl;
	cout << "差为:";
	c4.display();
	cout << endl;
	cout << "乘积为:";
	c5.display();
	cout << endl;
	cout << "商为:";
	c6.display();
	cout << endl;
	if (c1 == c2)
		cout << "c1和c2相等" << endl;
	else if (c1 != c2)
		cout << "c1和c2不相等" << endl;
}

(2). 自增和自减的重载

//自增和自减的实现
#include<iostream>
using namespace std;
class Counter
{
	int value;
public:
	Counter(){ value = 0; }
	Counter(int i) { value = i; }
	Counter& operator ++() //前置++,返回地址,是因为c++规定前置++返回地址,不然无法执行++a+=1这种操作
	{
		value++;
		return *this;//先加后用
	}
	const Counter operator ++(int)//int参数只起区分作用
	{
		Counter temp = *this;//先用后加
		++(*this);
		return temp;
	}
	friend int main();
	friend Counter operator !(Counter c);
};
//作为全局函数重载单目操作符
Counter operator ! (Counter c)
{
	Counter temp(!(c.value));
	return temp;
}
int main()
{
	Counter c1, c2, c3,c4;
	c2 = c1++;
	c3 = ++c1;
	c4 = !c1;
	cout << c1.value;
	cout << "c1:" << c1.value << endl;
	cout << "c2:" << c2.value << endl;
	cout << "c3:" << c3.value << endl;
	cout << "c4:" << c4.value << endl;
}

(3).特殊操作符的重载

1)."="赋值操作重载
//C++一些特殊操作符的重载
//1.“ = ”的重载(成员包含指针时,隐式的拷贝构造函数往往不能满足需求,会产生“孤儿”空间)
#include<iostream>
using namespace std;
class A
{
	int x, y;
	char* p;
public:
	A() { x = y = 0; p = NULL; }
	A(const char* str)
	{
		p = new char[strlen(str) + 1];
		strcpy(p, str);
		x = y = 0;
	}
	~A()
	{
		cout << "析构函数被调用。" << endl;
		delete[]p;
		p = NULL;
	}
	A(const A& x)
	{
		p = new char[strlen(x.p) + 1]; strcpy(p, x.p);

	}
     A& operator =(const A& a)
	{
		 cout << "此时调用赋值操作符函数。" << endl;
		 if (&a == this)return *this;//防止自身赋值
		 delete[]p;
		 p = new char[strlen(a.p) + 1];
		 strcpy(p, a.p);
		 x = a.x, y = a.y;
		 return *this;
	}
	 A& operator =(A&& x)//注意!!!实现该函数必须得实现拷贝构造函数!
	 {
		 cout << "此时调用转移赋值操作符函数。" << endl;
		 delete[]p;
		 p = x.p;
		 x.p = NULL;
		 return *this;
	 }
	 friend int main();
};

A f()
{
	A temp("ijkl");
	return temp;
}
int main()
{
	cout << "注意!实现转移赋值函数时必须实现拷贝构造函数!" << endl;
	A a1("abcd"), a2("efgh");//初始化
	a1 = a2;//调用 = 的重载函数
	a1 = f();//调用转移赋值函数
}
2)数组元素访问符的重载
#include<iostream>
using namespace std;
class String
{
	char* p;
public:
	String() { p = NULL; }
	String(const char str[]) { p = new char[strlen(str) + 1]; strcpy(p, str); }
	~String() { delete[]p; p = NULL; }
public:
	char& operator [](int i)
	{
		if (i < 0 || i >= strlen(p))
		{
			cerr << "数组下标越界!" << endl;
			exit(-1);
		}
		return p[i];
	}
};
int main()
{
	String s("abcd");
	for (int i = 0; i < 4; i++)
		cout << "s[" << i << "]: " << s[i] << endl;
}
3)"new"的重载
//new有两个功能:为动态对象分配空间和调用对象类的构造函数
//重载的是它分配空间的功能,不影响对构造函数和析构函数的调用
//在c++中,new只能作为类的静态成员来重载(static说明可以不写)

//格式为:void *operator new(size_t size,...);
//返回类型必须为void *,第一个参数size是需要分配的内存空间大小,其类型为size_t(unsigned int),其他参数可有可无
#include<cstring>
#include<cstdlib>
class A
{
	int x, y;
public:
	void* operator new(size_t size)
	{
		void* p = malloc(size);//申请堆空间
		memset(p, 0, size);//memset为c++标准库函数,把申请到堆的空间初始化为全“0”;
		return p;
	}
};
class B
{
	int x, y;
public:
	B() { x = y = 0; }
	B(int i) { x = y = i; }
	void* operator new(size_t size,void *p)//也可以有多个参数
	{
		return p;
	}
	friend int main();
};
int main()
{
	A* p = new A;
    char buf[sizeof(B)];
	B* p1 = new (buf)B(0);//含有其他参数时,创建方式为:A*p=new(...)A;
}

上面操作符new重载函数的行为与系统提供的new功能差不多,区别在于它对申请来的空间进行了初始化,其好处是为一个没有任何构造函数的类的对象提供初始化。

对于上面动态对象的创建,其内存空间不是在程序的堆区分配,而是由创建者提供的。对于这样的动态 对象,如果要使其消亡,则不应该用delete操作,而是通过直接调用对象类的析构函数来实现:

p->~A();

如果使用delete操作来撤销上面的动态对象,将会导致程序异常终止,这是因为系统提供的delete操作将会把对象的空间归还到程序的堆区中,而该对象的空间不是在程序堆区中分配的。

4)“delete”的重载

一般来说,如果对某个类重载了操作符new,则相应地也要重载操作符delete。操作符delete也必须作为静态的成员函数来重载,其格式为:void operator delete(void *p,size_t size);

<*>用重载操作符new与delete来管理某类对象的堆空间

内存管理,狗都不做

#include<cstring>
#include<cstdlib>
const int NUM = 12;
class A
{
public:
	static void* operator new(size_t size);
	static void operator delete(void* p);
private:
	static A* p_free;
	A* next;
};
A* A::p_free = NULL;
void* A::operator new(size_t size)
{
	A* p;
	if (p_free == NULL)
	{
		p_free = (A*)malloc(size * NUM);
		for (p = p_free; p != p_free + NUM - 1; p++)
			p->next = p + 1;
		p->next = NULL;
	}
	p = p_free;
	p_free = p_free->next;
	memset(p, 0, size);//把对象空间初始化为全“0”。如果类中有构造函数,这个操作可以不要
	return p;
}
void A::operator delete(void* p)
{
	((A*)p)->next = p_free;
	p_free = (A*)p;
}
5)函数调用操作符"()"的重载
#include<iostream>
using namespace std;
class A
{
	int value;
public:
	A() { value = 0; }
	A(int i) { value = i; }
	int operator ()(int x)
	{
		cout << "此时调用函数调用重载。" << endl;
		return x + value;
	}
};
int main()
{
	A a(3);//初始化a为3
	cout << a(10) << endl;//可以看作调用函数“a”,应该输出13
}

函数调用操作符重载主要应用于只有一个操作的对象——函数对象,该对象除了具有一般的函数行为外,它还可以拥有状态,可以实现含有static存储类的局部变量的全局函数功能。例如,下面的函数对象random实现了随机函数的功能:

#include<iostream>
using namespace std;
class Random
{
	unsigned int seed;
public:
	Random(unsigned int i)
	{
		seed = i;
	}
	unsigned int operator()()
	{
		seed = (25173 * seed + 13849) % 65536;
		return seed;
	}
};
int main()
{
	Random random(1);//改变初始化的值将会得到不同的随机数
	cout << random();
}
6)间接类成员访问操作符”->“重载

通过对其进行重载可以实现一种“智能指针”,用该指针另访问一个对象的访问成员时,能在访问前做一些额外的事情

//实现在程序执行的某个时刻能够获得一个对象的访问次数
#include<iostream>
using namespace std;
class A
{
	int x, y;
public:
	void f() { cout << "这是函数f。" << endl; }
	void g() { cout << "这是函数g。" << endl; }
};
class B//智能指针类
{
	A* p_a;
	int count;
public:
	B(A* p)
	{
		p_a = p;
		count = 0;
	}
	~B()
	{
		p_a = NULL;
	}
	A* operator ->()
	{
		count++;
		return p_a;
	}
	int num_of_A_access()const
	{
		return count;
	}
};
void func(B& p)
{
	p->f();
	p->g();
}
int main()
{
	A a;
	B b(&a);
	func(b);
	cout << "借由智能指针得出访问对象a的次数为:" << b.num_of_A_access() << endl;
}
7)类型转换操作符重载
<1>.带一个参数的构造函数用作类型转换

带一个参数的构造函数可以用作从一个基本数据类型或其他类到一个类的转换。

#include<iostream>
using namespace std;
class Complex
{
	double real, imag;
public:
	Complex() { real = imag = 0; }
	Complex(double r) { real = r, imag = 0; }
	Complex(double r, double i) { real = r, imag = i; }
	friend Complex operator +(const Complex& x, const Complex& y);
};
Complex operator +(const Complex& x, const Complex& y)
{
	Complex temp;
	temp.real = x.real + y.real;
	temp.imag = x.imag + y.imag;
	return temp;
}
int main()
{
	Complex c1(1, 2), c2, c3;
	c2 = c1 + 1.7;//1.7隐式转换成一个复数对象Complex(1.7)
	c3 = 2.5 + c2;//2.5隐式转换成一个复数对象Complex(2.5)
}
<2>.自定义类型转换
#include<iostream>
using namespace std;
class A
{
	int x, y;
public:
	operator int() { return x + y; }
};
int main()
{
    A a;
	int i = 1;
	int z = i + a;//将调用类型转换操作符int的重载函数
}
<3>.上述两点歧义问题的解决

当一个类中同时定义了具有一个参数(t类型)的构造函数和t类型转换操作符重载函数时,将会产生歧义问题。例如:

class A
{
	int x, y;
public:
    A(){x=0;y=0;}
    A(int i){x=i;y=0;}
	operator int() { return x + y; }
	friend A operator +(const A& a1, const A& a2);
};
A operator +(const A& a1, const A& a2)
{
	A temp;
	temp.x = a1.x + a2.x;
	temp.y = a1.y + a2.y;
}
int main()
{
    A a;
    int i=1,z;
    z=a+i;//是a转换成int还是i转换成A?
}

可以用显示类型转换解决:

z=int(a)+i;

z=a+A(i);

也可以通过给A类的构造函数A(int i)前加一个修饰符explicit来解决:

explicit A(int i){x=i,y=0;}

explicit的含义是:禁止把A(int i)当作隐式类型转换符使用。这样,上面的“a+i;”就只有一个含义:把a转成int。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这本经典、畅销的数据结构教材详细介绍了数据抽象的基础知识,强调作为面向对象方法基础原理的规范和实施之间的区别。书中使用的软件工程原则和概念以及UML图便于增强学生的理解。 ◆ 详细介绍了数据抽象,强调规范和实现之间的区别 ◆ 广泛介绍了各种面向对象的编程技术 ◆ 重点是核心的数据结构,而不是非必要的C++语言语法 ◆ 说明了和ADT在问题解决过程中的作用 ◆ 诠释了ADT的主要应用,如查找航班图、事件驱动的模拟和八皇后问题 ◆ 大部分章节中的例子都使用了标准模板库(STL) ◆ 介绍了递归 ◆ 附录中提供了基本的C++语法,以帮助学生从其他语言转换为C++ 第1章 数据抽象:墙 1 1.1 面向对象的概念 2 1.1.1 面向对象分析与设计 2 1.1.2 面向对象解决方案的特征 3 1.2 获得更好的解决方案 4 1.2.1 内聚 5 1.2.2 耦合 5 1.3 规范 6 1.3.1 操作契约 7 1.3.2 特殊情况 8 1.3.3 抽象 9 1.3.4 信息隐藏 10 1.3.5 最小且完整的接口 11 1.4 抽象数据型 12 1.4.1 设计ADT 14 1.4.2 涉及其他ADT的ADT 17 1.5 ADT包 18 1.5.1 确定行为 18 1.5.2 指定数据和操作 19 1.5.3 ADT的模板接口 22 1.5.4 使用ADT包 24 C++片段1 C++ 29 C1.1 待解决的问题 30 C1.1.1 私有数据字段 31 C1.1.2 构造函数和析构函数 32 C1.1.3 方法 32 C1.1.4 防止编译错误 33 C1.2 实现解决方案 34 C1.3 模板 35 C1.4 继承 37 C1.4.1 基和派生 38 C1.4.2 重写基方法 40 C1.5 虚方法和抽象类 42 C1.5.1 虚方法 42 C1.5.2 抽象类 43 第2章 递归:镜子 45 2.1 递归解决方案 46 2.2 返回值的递归 48 2.2.1 递归值函数:n的阶乘 49 2.2.2 箱式跟踪 52 2.3 执行动作的递归 55 2.4 递归与数组 62 2.4.1 逆置数组项 63 2.4.2 折半查找 64 2.4.3 查找数组中的最大值 68 2.4.4 查找数组中第k个最小值 69 2.5 组织数据 71 2.6 更多示例 75 2.6.1 Fibonacci数列(兔子繁殖) 75 2.6.2 组织游行队伍 78 2.6.3 从n个事物中选出k个 79 2.7 递归和效率 81 第3章 基于数组的实现 91 3.1 办法 92 3.1.1 核心方法 93 3.1.2 使用大小固定的数组 93 3.2 ADT包的基于数组的实现 94 3.2.1 头文件 95 3.2.2 定义核心方法 96 3.2.3 测试核心方法 98 3.2.4 实现更多方法 101 3.2.5 删除项的方法 103 3.2.6 测试 106 3.3 在实现中使用递归 107 3.3.1 getIndexOf方法 107 3.3.2 getFrequencyOf方法 108 C++片段2 指针、多态和内存分配 113 C2.1 变量的内存分配和方法的前期绑定 114 C2.2 需要解决的问题 115 C2.3 指针与程序的自由存储 116 C2.3.1 释放内存 118 C2.3.2 避免内存泄漏 119 C2.3.3 避免悬挂指针 122 C2.4 虚方法和多态 124 C2.5 数组的动态分配 126 第4章 基于链表的实现 129 4.1 预备知识 130 4.2 ADT包的基于链表的实现 133 4.2.1 头文件 134 4.2.2 定义核心方法 135 4.2.3 实现更多方法 138 4.3 在基于链表的实现中使用递归 143 4.4 测试多个ADT实现 145 4.5 比较基于数组的实现和基于链表的实现 148 第5章 作为问题求解技术的递归 155 5.1 定义语言 156 5.1.1 语法知识基础 156 5.1.2 两种简单的语言 158 5.2 代数表达式 160 5.2.1 代数表达式的型 160 5.2.2 前缀表达式 162 5.2.3 后缀表达式 166 5.2.4 完全括号化表达式 168 5.3 回溯 168 5.3.1 查找航线 168 5.3.2 八皇后问题 173 5.4 递归和数学归纳法的关系 179 5.4.1 递归阶乘函数的正确性 179 5.4.2 Hanoi塔的工作量 180 第6章 栈 189 6.1 ADT栈 190 6.1.1 在设计解决方案期间开发ADT 190 6.1.2 ADT栈的规范 192 6.2 栈的简单应用 197 6.2.1 检查括号匹配 197 6.2.2 识别语言中的字符串 199 6.3 栈在代数表达式中的应用 200 6.3.1 计算后缀表达式 201 6.3.2 中缀表达式与后缀表达式的等价转换 202 6.4 使用栈查找航班图 205 6.5 栈和递归的关系 212 C++片段3 异常 221 C3.1 背景知识 222 C3.2 断言 223 C3.3 抛出异常 224 C3.4 处理异常 227 C3.4.1 多个catch块 228 C3.4.2 未捕获的异常 229 C3.5 程序员定义的异常 232 第7章 实现ADT栈 235 7.1 基于数组的实现 236 7.2 基于链表的实现 239 7.3 在实现中使用异常 243 第8章 列表 247 8.1 指定ADT列表 248 8.2 使用列表操作 252 8.3 ADT列表的模板接口 255 第9章 实现列表 259 9.1 基于数组的ADT列表实现 260 9.1.1 头文件 261 9.1.2 实现文件 262 9.2 基于链表的ADT列表实现 266 9.2.1 头文件 266 9.2.2 实现文件 268 9.2.3 在LinkedList的方法中使用递归 275 9.3 两种实现的比较 279 第10章 算法的效率 283 10.1 什么是好的解决方案 284 10.2 测量算法的效率 285 10.2.1 算法的执行时间 286 10.2.2 算法增长率 287 10.2.3 分析与大O表示法 288 10.2.4 正确分析问题 291 10.2.5 查找算法的效率 293 第11章 排序算法及其效率 299 11.1 基本排序算法 300 11.1.1 选择排序 300 11.1.2 起泡排序 303 11.1.3 插入排序 305 11.2 较快排序算法 307 11.2.1 归并排序 307 11.2.2 快速排序 312 11.2.3 基数排序 319 11.3 各种排序算法的比较 321 C++片段4 关系和重用 325 C4.1 回顾继承 326 C4.1.1 的公有、私有和受保护部分 331 C4.1.2 公有、私有和受保护继承 332 C4.1.3 is-a和as-a关系 333 C4.2 包含:has-a关系 334 C4.3 回顾抽象基 335 第12章 有序表及其实现 339 12.1 指定ADT有序表 340 12.1.1 ADT有序表的模板接口 342 12.1.2 使用有序表的操作 343 12.2 基于链表的实现 344 12.2.1 头文件 344 12.2.2 实现文件 345 12.2.3 基于链表的实现的效率 348 12.3 使用ADT列表的实现 348 12.3.1 包含 349 12.3.2 公有继承 352 12.3.3 私有继承 356 第13章 队列和优先队列 363 13.1 ADT队列 364 13.2 ADT队列的简单应用 367 13.2.1 读取字符串 367 13.2.2 识别回文 368 13.3 ADT优先队列 369 13.4 应用:模拟 371 13.5 面向位置和面向值的ADT 379 第14章 队列和优先队列的实现 387 14.1 ADT队列的实现 388 14.1.1 使用ADT列表的实现 388 14.1.2 基于链表的实现 390 14.1.3 基于数组的实现 394 14.1.4 比较实现 399 14.2 ADT优先队列的实现 400 C++片段5 运算符重载和友元访问 405 C5.1 重载运算符 406 C5.1.1 重载=进行赋值 408 C5.1.2 重载+进行连接 410 C5.2 友元访问和<<的重载 411 第15章 树 415 15.1 术语 416 15.1.1 树的型 417 15.1.2 树的高度 419 15.1.3 满二叉树、完全二叉树和平衡二叉树 421 15.1.4 二叉树的最大和最小高度 422 15.2 ADT二叉树 425 15.2.1 二叉树的遍历 425 15.2.2 二叉树的操作 428 15.2.3 ADT二叉树的模板接口 430 15.3 ADT二叉查找树 432 15.3.1 二叉查找树的操作 433 15.3.2 查找二叉查找树 434 15.3.3 创建二叉查找树 435 15.3.4 遍历二叉查找树 437 15.3.5 二叉查找树操作的效率 437 第16章 树的实现 443 16.1 二叉树中的节点 444 16.1.1 基于数组的表示 444 16.1.2 基于链表的表示 446 16.2 ADT二叉树基于链表的实现 447 16.2.1 头文件 447 16.2.2 实现 450 16.3 ADT二叉查找树基于链表的实现 458 16.3.1 ADT二叉查找树操作的算法 458 16.3.2 BinarySearchTree 469 16.4 在文件中保存二叉查找树 471 16.5 树排序 474 16.6 一般树 474 C++片段6 迭代器 479 C6.1 迭代器 480 C6.1.1 常见的迭代器操作 481 C6.1.2 使用迭代器操作 482 C6.1.3 实现迭代器 483 C6.2 迭代器的高级功能 485 第17章 堆 489 17.1 ADT堆 490 17.2 堆的基于数组的实现 493 17.2.1 基于数组的堆操作的算法 494 17.2.2 实现 498 17.3 ADT优先队列的堆实现 502 17.4 堆排序 504 第18章 字典及其实现 511 18.1 ADT字典 512 18.2 可能的实现 517 18.2.1 ADT字典的基于数组的有序实现 519 18.2.2 ADT字典的二叉查找树实现 521 18.3 选择实现 523 18.4 散列 529 18.4.1 散列函数 532 18.4.2 解决冲突 534 18.4.3 散列的效率 539 18.4.4 如何确立散列函数 542 18.4.5 字典遍历:散列的低效操作 543 18.4.6 使用散列和分离链实现ADT字典 544 第19章 平衡查找树 551 19.1 平衡查找树 552 19.2 2-3树 553 19.2.1 遍历2-3树 555 19.2.2 查找2-3树 556 19.2.3 在2-3树中插入数据 558 19.2.4 从2-3树中删除数据 562 19.3 2-3-4树 567 19.3.1 查找和遍历2-3-4树 569 19.3.2 在2-3-4树中插入数据 569 19.3.3 从2-3-4树中删除数据 572 19.4 红-黑树 573 19.4.1 查找和遍历红-黑树 575 19.4.2 红-黑树的插入和删除 575 19.5 AVL树 577 第20章 图 583 20.1 术语 584 20.2 将图作为ADT 587 20.3 图的遍历 591 20.3.1 深度优先查找 592 20.3.2 广度优先查找 593 20.4 图的应用 595 20.4.1 拓扑排序 595 20.4.2 生成树 598 20.4.3 最小生成树 600 20.4.4 最短路径 603 20.4.5 回路 606 20.4.6 一些复杂问题 608 第21章 外部存储中的数据处理 615 21.1 了解外部存储 616 21.2 排序外部文件的数据 618 21.3 外部字典 624 21.3.1 确定外部文件的索引 626 21.3.2 外部散列 629 21.3.3 B-树 632 21.3.4 遍历 639 21.3.5 多索引 640 C++片段7 标准模板库 647 C7.1 STL容器 648 C7.1.1 STL容器适配器 649 C7.1.2 顺序容器 650 C7.1.3 关联容器 654 C7.2 STL算法 657 附录A 回顾C++基础 659 附录B 编程中的重要主题 697 附录C 统一建模语言 719 附录D 软件生命周期 727 附录E 数学归纳法 733 附录F 算法验证 737 附录G C++文件基础 741 附录H C++头文件和标准函数 751 附录I C++文档系统 755 附录J ASCII字符代码 757 附录K 针对Java编程人员的C++知识 759 附录L 针对Python编程人员的C++知识 767
在面向对象的编程中,C语言并不直接支持和抽象的概念。引用中提到,final关键字用来修饰方法,表示该方法不能在子中被覆盖。而abstract关键字用来修饰抽象方法,表示该方法必须在子中被实现。然而,在C语言中,没有对应的关键字来实现和抽象的概念。 相反,C语言通过结构体来模拟的概念。结构体是一种用户自定义的数据型,可以包含多个不同型的数据成员。通过结构体,我们可以将相关的数据和功能组合在一起。然而,C语言中的结构体不支持继承和多态等面向对象的特性。 在C语言中,我们可以使用函数指针来模拟抽象类和接口的概念。函数指针可以指向不同的函数,通过使用函数指针,我们可以实现多态性,即在运行时根据函数指针指向的具体函数来执行不同的操作。 综上所述,C语言并不直接支持面向对象中的和抽象的概念,但可以使用结构体和函数指针来实现似的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [面向对象——对象](https://blog.csdn.net/shouyeren_st/article/details/126210622)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [面向对象编程原则(06)——依赖倒转原则](https://blog.csdn.net/lfdfhl/article/details/126673771)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值