C++复习

1.C++与C语言的区别

1.C语言头文件<XXX.h>,C++后缀不带.h

2.C++中有bool类型(整型) / 单独占一个字节 / 取值true(1)false(0)

2.引用

2.1 特点

1.引用实体与引用类型必须为同一种类型

2.引用在定义时必须初始化并且不能被初始化为NULL

3.不可以改变引用关系

注:被const修饰的变量其引用也要被const修饰

2.2 参数加以引用的原因

1.可以在函数内部对此参数进行修改

2.提高函数调用和运行效率

以引用作为返回值的好处:在内存中不产生被返回值的副本

2.3 引用和指针的区别

1.引用在定义时必须初始化并且不能被初始化为NULL,指针没有要求

2.引用不能改变引用关系,指针随意

3.存在多级指针(**p)没有多级引用

4.引用的++和指针的++表达的含义不同,指针++(自增)是指针上的计算,引用++单纯+1

5.指针用sizeof计算的大小结果不同,指针在32位内存下是4个字节,64位内存下是8字节,引用的sizeof结果以引用类型大小决定

3.重载函数

C++中在同一个作用域下某个函数/运算符可以指到多个定义

在同一个作用域内可以声明多个功能类似的同名函数,但这些同名函数的形参的个数,类型,顺序必须不同,不能仅通过函数返回值类型不同来重载函数

#include<iostream>
#include<vector>
#include<string>
using namespace std;
//函数名相同,但参数的类型、数量、顺序不同
void add(int a,int b)
{
	cout << "int a  int b" << endl;
}
void add(double a,int b)
{
	cout << "double a   int b" << endl;
}
int main()
{
	add(1, 2);//输出int a   int b
	add(0.1, 5);//输出 double a   int b
	return 0;
}

4.函数参数默认值

函数声明/定义时可以直接给形参赋值(必须从右向左依次赋值)若在调用函数时未给参数传值,会使用给定的默认值

#include<iostream>
#include<vector>
#include<string>
using namespace std;
//函数名相同,但参数的类型、数量、顺序不同
void add(int a ,int b = 2)
{
	cout << a << b << endl;
}
void add1(double a = 0.1,int b)//报错,应从右向左赋值
{
	cout << "double a   int b" << endl;
}
int main()
{
	add(1);//输出1 2
	
	return 0;
}

5.vector数组

1.常用函数

back(   ) 返回最末一个元素

swap(  ,  )  交换两个元素的值

push_back( ) 在最后添加一个元素

pop_back( ) 移除最后一个元素

front( ) 返回第一个元素

empty( ) 判断Vector是否为空 (true为空,false非空)

clear( ) 清空所有元素

2.迭代器

#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
	vector<int> array;
	int i = 0;
	for ( i = 0; i < 10 ; i++)
	{
		array.push_back(i);
	}             
	for (vector<int>::iterator it = array.begin();it != array.end();it++)
		             //interator 迭代器
	{
		cout << *it << endl;
	}

	return 0;
}

6.面向对象三大特征

1.封装:将属性(成员变量)和操作(成员函数)结合为一个独立的整体。

2.继承:子类继承父类的特征和行为,使得子类对象具有父类的实例域和方法,或者子类从父类继承方法,使得子类具有父亲相同的行为

3.多态:多态是同一个行为具有多个不同的表现形式或形态的能力

声明和定义的区别

声明是告诉编译器变量或函数的类型和名字,不会为变量分配空间

定义就是对这个变量或函数进行内存分配和初始化,需要分配空间,同一个变量可以被声明多次,但是只能被定义一次

构造函数

没有返回值  语法: 类名(){ }

函数名称与类名相同

构造函数可以有参数,可以重载

编译器在创建对象的时候会自动调用构造函数

初始化参数列表

只能在构造函数里使用该语法,可以给所有成员设置初始化参数,const类型和引用类型必须在初始化参数列表中初始化。成员的构造顺序和在初始化参数列表中的顺序无关,与在类中声明顺序有关

析构函数

析构函数是作用域对象销毁工作,清空对象内部指针指向的堆区内存,析构函数在释放对象的时候自动调用,栈区对象自动释放。

语法: 无返回值   ~类名( ) { }

           函数名称与类名相同,但是前面要有~

           析构函数不可以有参数,因此不能发生重载

           编译器在对象销毁前会自动调用析构函数,不需要手动调用

函数重写(覆盖)

子类重新定义父类中有相同名称,返回值和参数的虚函数,主要在继承关系中出现

基本条件:

重写的函数和被重写的函数都必须为virtual函数,并分别位于基类和派生类中

重写的函数和被重写的函数,返回值,函数名和函数参数必须完全一致

函数隐藏

在父类和子类中,函数名相同,参数不同,无论父类中的童工函数是否含有virtual关键字,都是隐藏

函数隐藏和函数重写的区别:在父类和子类中,函数名相同,参数相同,父类中的同名函数没有virtual关键字为隐藏

在子类中只要和父类函数名字相同不是重写,一定是函数隐藏

多态 & 虚函数表

多态按字面意思就是多种形态,当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

·静态多态

函数重载(运算符重载),模板属于静态多态,在编译期间就能确定的多态

·动态多态

父类指针或引用指向子类对象,通过指针或引用调用子类重写的虚函数,在运行期间才能确定具体调用哪个函数,是动态多态

·启动动态多态的条件

有继承关系,子类重写父类虚函数并且父类指针调用子类重写的虚函数

#include<iostream>
using namespace std;
//重写:子类和父类中,同名且同形参的虚函数能够发生重写
//子类重写父类的虚函数(virtual)
class A
{
public:
	int a;
	int b;
	int c;
	const int d = 1;
	virtual void work()
	{
		cout << "A work()" << endl;
	}
	void fun()
	{
		cout << "A fun()" << endl;
	}
};
class B : public A
{
public:
	virtual void work()
	{
		cout << "B重写了父类A的work函数" << endl; //函数重写
	}
	void fun()
	{
		cout << "B fun()" << endl; //函数隐藏
	}
};
class C : public B
{
public:
	void work()
	{
		cout << "C重写了父类B的work函数" << endl; //函数重写
	}
	void fun()
	{
		cout << "C fun" << endl; //函数隐藏
	}
};
int main()
{
	A* a = new C();
	a->work(); //函数重写 生成"C重写了父类B的work函数"
	a->fun();  //函数隐藏 生成"A fun()"
	A b;
	b.fun(); //生成"A fun()"
	b.work(); //生成"A work()"
	B* c = new C();
	c->fun(); //生成"B fun()"
	c->work();  //函数重写 生成"C重写了父类B的work函数"
	return 0;
}

多态的实现

为了实现C++的多态,C++使用了一种动态绑定的技术。这个技术的核心是虚函数表,下面介绍虚函数表是如何实现动态绑定的。

类的虚函数表

每个包含了虚函数的类都包含一个虚表(存放虚函数指针的数组)

当一个类(B)继承另一个类(A)时,类B会继承类A的函数调用权,所以如果一个基类包含了虚函数,那么其继承类也可以调用这些虚函数,换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表

以下代码,类A包含了虚函数,故A拥有一个虚表,类B继承类A,同时继承了类A的函数调用权,所以类B也有一个虚表

class A {
	//由于类A中有虚函数,所以类A拥有一个虚表
public:
	virtual void vfunc1();
	virtual void vfunc2();
	void func1();
	void func2();
private:
	int m, n;
};
class B :public A {
	//此时类B也拥有自己的虚表
};

虚表是一个存放指针的数组,其内的元素是虚函数的指针,每个元素对应一个虚函数的函数指针,需要指出的是:普通的函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。

虚表内的条目,即虚函数指针的赋值发生在编译器的编译阶段,也就是说在代码的编译阶段,虚表就可以构造出来了

虚表指针

虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可,同一个类的所有对象都使用同一个虚表。

为了指定对象的虚表,对象内部包含一个虚表的指针来指向自己所使用的虚表,为了让每个包含虚表的类的对象都有一个虚表指针,编译器在类中添加了一个指针,*_vptr,用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动设置为指向类的虚表。

验证_vptr的方法就是先求一个普通类占的字节大小sizeof(),然后将类中某一个函数前加virtual关键字变为虚函数,再求该类占的字节数大小sizeof(),会发现增加了4个字节,这就验证了vptr的存在

 动态绑定

动态联编(动态绑定)是指编译程序在编译阶段并不能确切地知道将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切的知道将要调用的函数,要求联编工作在程序运行时进行,这种在程序运行时进行的联编工作成为动态联编。

C++规定:动态联编是在虚函数的支持下实现的

动态联编必须包括以下方面:

1、成员函数必须声明为virtual

2、如果基类中声明了为虚函数,则派生类中不必再声明

调用方式:

  通过对象的指针或引用调用成员函数,或通过成员函数调用,繁殖就无法实现动态联编

特点:

  灵活、问题抽象性和问题的易维护性

#include<iostream>
using namespace std;
class A {
public:
	virtual void vfunc1();
	virtual void vfunc2();
	void func1();
	void func2();
private:
	int m, n;
};
class B :public A {
public:
	virtual void vfunc1();//重写
	void func1();
private:
	int a;
};
class C : public B {
public:
	virtual void vfunc2();
	void func2();
private:
	int m,b;
};

 图为类A,类B,类C的对象模型

        由于这三个类都有虚函数,故编译器为每个类都创建了一个虚表,即类A的虚函数表           (A vtbl),类B的虚函数表(B vtbl),类C的虚表(C vtbl)。类A,类B,类C的对象都拥有一个虚表指针*_vpter,用来指向自己所属类的虚表。

类A包括两个虚函数,故A vtbl包含两个指针,分别指向A::vfunc1()和A::vfunc2

类B继承于类A,故类B可以调用类A的函数,但由于类B重写了B::vfunc1(),故B vtbl的两个指针分别指向B::vfunc1()和A::vfunc2()

类C继承于类B,故类C可以调用类B的函数,但由于类C重写了C::vfunc2()函数,故C vtbl的两个指针分别指向B::vfunc1()(指向继承的最近的一个类的函数)和C::vfunc2()

虚表中的指针会指向其继承的最近的一个类的虚函数

非虚函数的调用不经过虚表,所以不需要虚表中的指针指向这些函数

创建链表:

main.cpp

#include"MyList.h"
int main()
{
	vector<int> vec = { 1,2,3,4,5 };

	return 0;
}

MyList.h

#pragma once
#include<iostream>
#include<vector>
using namespace std;
struct ListNode
{
	//数据域
	int val;
	//指针域
	ListNode* next;
	ListNode(int val)
	{
		this->val = val;
		next = nullptr;
	}

};
class MyList
{
	ListNode* Head = nullptr;
	ListNode* Tail = nullptr;
public:
	MyList();
	MyList(vector<int> vec);
};

MyList.cpp

#include "MyList.h"
MyList::MyList()
{

}
MyList::MyList(vector<int> vec)
{
	if (vec.size() == 0) 
	{
		return;
	}
	Head = new ListNode(vec[0]);
	Tail = Head;
	for (int i = 1; i < vec.size(); i++)
	{
		ListNode* node = new ListNode(vec[i]);
		Tail->next = Tail;
	}
}

——————————————————补——充——————————————————-——

#include<iostream>
#include<stack>
using namespace std;
int main()
{
	stack<int> stk;
	for (int i = 1;i < 5;i++)
	{
		stk.push(i);
		//在栈顶部增加元素
	}
	while (!stk.empty())
	{
		cout << stk.top() << endl;
		//访问栈顶元素
		stk.pop();
		//移除栈顶元素
	}
	return 0;
}

——————————————————补——充——————————————————-——

数是一种数据结构,它是由n(n>=1)个有限节点组成的一个具有层次关系的集合

特点

每个节点有零个或多个子节点

没有父节点的结点成为根节点

每个非根节点有且只有一个父结点

除了根节点外,每个子节点可以分为多个不相交的子树

树的术语

结点的度:

结点所拥有子树的数目

叶子:

度为零的结点

分支结点:

度不为零的结点

树的度:

树中结点的最大的度

层次:

根结点的层数为1,其余结点的层次等于该结点的父亲结点的层次+1

树的高度:

树中结点的最大层次

无序树:

如果树中结点的各子节点之间的次序是不重要的,可以交换位置

有序树:

如果树中结点的各子树之间的次序是重要的,不可以交换位置

森林:

0个或多个不相交的树组成,对森林加上一个根,森林即成为树,删去根,树即为森林

二叉树的性质:

二叉树第i层上的结点数最多为2的i-1次方个

深度为k的二叉树最多有2的k次方-1个结点

包含n个结点的二叉树的高度至少为log2(n+1)

在任意一颗二叉树中,若叶子结点的个数为n0,度为2的节点数为n2,则n0=n2+1

二叉搜索树

二叉搜素树又叫二叉查找树,其中每个节点都有一个键标识该节点唯一,并且每个键大于左子树上任意节点的键,小于右子树上任意节点的键

特点:

若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值

任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值

任意结点的左、右子树分别为二叉查找树

没有键值相等的结点

__________________________________________________________________

树的创建:

main.cpp

#include<iostream>
#include<vector>
#include"Tree.h"
using namespace std;
int main()
{
	vector<int> res = { 7,4,8,6,4,1,3 };
	Tree t(res);
	t.Level(t.get_root());
	return 0;
}

Tree.cpp

#include"Tree.h"
Tree::Tree(vector<int> &vec)
{
	root = new Node(vec[0]);
	for (int i = 1;i<vec.size();i++)
	{
		Node* p = root;
		while (1)
		{
			if (vec[i] < p->val)
			{
				if (p->left == nullptr)
				{
					p->left = new Node(vec[i]);
					break;
				}
				p = p->left;
			}
			if (vec[i] > p->val)
			{
				if (p->right == nullptr)
				{
					p->right = new Node(vec[i]);
					break;
				}
				p = p->right;
			}
			if (vec[i] == p->val)
			{
				cout << "出现重复值,跳过此数" << endl;
				break;
			}
		}
	}
		
}

Tree.h

#pragma once
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
struct Node
{
	int val;
	Node* left = nullptr;
	Node* right = nullptr;
	Node(int val)
	{
		this->val = val;
	}

};
class Tree
{
private:
	Node* root = nullptr;
public:
	Tree(vector<int>& vec);
	Node* get_root() { return root; }
	void Level(Node* root)
	{
		queue<Node*> que;//创建队列
		que.push(root);
		while (!que.empty()) //如果队列为空则返回1,不为空则返回0
		{
			int n = que.size(); 
			for (int i = 0; i < n; i++)
			{
				Node* front = que.front(); //
				cout << front->val << "  ";
				que.pop();
				if (front->left) que.push(front->left);
				if (front->right) que.push(front->right);
			}
			cout << endl;
		}
	}
};

输出结果:

树的遍历

先序遍历:

void Tree::PreOrder() //先序遍历
{
	stack<Node*> stk;
	stk.push(root);
	while (!stk.empty())
	{
		Node* temp = stk.top();
		stk.pop();
		cout << temp->val << "  ";
		if (temp->right) stk.push(temp->right);
		if (temp->left) stk.push(temp->left);
	} 
}

中序遍历:

void Tree::InOrder()//中序遍历
{
	stack<Node*> stk;
	Node* cur = root;
	while (cur || !stk.empty())
	{
		while (cur)
		{
			stk.push(cur);
			cur = cur->left;
		}
		Node* temp = stk.top();
		stk.pop();
		cout << temp->val << "  ";
		cur = temp->right;
	}
}

 后序遍历:

void Tree::PosOrder()//后序遍历
{
	stack<Node*> s1, s2;
	s1.push(root);
	while (s1.size())
	{
		Node* temp = s1.top();
		s1.pop();
		s2.push(temp);
		if (temp->left) s1.push(temp->left);
		if (temp->right) s1.push(temp->right);
	}
	while (s2.size())
	{
		cout << s2.top()->val << "  ";
		s2.pop();
	}
}

层级遍历:

void Level(Node* root)
	{
		queue<Node*> que;//创建队列
		que.push(root);
		while (!que.empty()) //如果队列为空则返回1,不为空则返回0
		{
			int n = que.size(); 
			for (int i = 0; i < n; i++)
			{
				Node* front = que.front(); //
				cout << front->val << "  ";
				que.pop();
				if (front->left) que.push(front->left);
				if (front->right) que.push(front->right);
			}
			cout << endl;
		}
	}

排序

归并排序:

#include<iostream>
#include<vector>
using namespace std;
void Mery(vector<int>& vec, int L, int mid, int R)
{
	vector<int> temp(R - L + 1);
	int i = L, j = mid + 1, index = 0;
	while (i <= mid && j <= R)
	{
		if (vec[i] <= vec[j])
		{
			temp[index++] = vec[i++];
		}
		else
		{
			temp[index++] = vec[j++];
		}
	}
	while (i <= mid)
	{
		temp[index++] = vec[i++];
	}
	while (j <= R)
	{
		temp[index++] = vec[j++];
	}
	index = L;
	for (int i = 0; i < (R - L + 1); i++)
	{
		vec[index++] = temp[i];
	}
}
void Merg_Sort(vector<int> &vec,int L, int R)
{
	if (L >= R) return;
	int mid = (R - L) / 2 + L;
	Merg_Sort(vec, L, mid);
	Merg_Sort(vec, mid + 1, R);
	Mery(vec, L, mid, R);
}
int main()
{
	vector<int> vec = { 3,4,5,1,7,8,6,4 };
	Merg_Sort(vec, 0, vec.size() - 1);
	return 0;
}

快排:

#include<iostream>
#include<vector>
using namespace std;
pair<int, int> Quick(vector<int>& vec, int L, int R)
{
	int temp = vec[L];
	int i = L - 1;
	int j = R + 1;
	int index = L;
	while (index < j)
	{
		if (vec[index] == temp)
		{
			index++;
		}
		else if (vec[index] > temp)
		{
			swap(vec[--j], vec[index]);
		}
		else
		{
			swap(vec[++i], vec[index++]);
		}
	}
	return make_pair(i, j);
}
void Quick_Sort(vector<int>& vec, int L, int R)
{
	if (L >= R) return;
	pair<int, int> p = Quick(vec, L, R);
	Quick_Sort(vec, L, p.first);
	Quick_Sort(vec, p.second, R);
}
int main()
{
	vector<int> vec = { 5,4,2,5,6,8,1,0,9 };
	Quick_Sort(vec, 0, vec.size() - 1);
	for (auto it : vec)
	{
		cout << it << "  ";
	}
	return 0;
}

冒泡

#include<iostream>
#include<vector>
#include<string>
using namespace std;
void Maopao_sort(vector<int>& nums)
{
	for (int i = 1; i < nums.size(); i++)
	{
		for (int j = 0; j < nums.size()-i; j++)
		{
			if (nums[j] > nums[j + 1]) swap(nums[j], nums[j + 1]);
		}
	}

}
int main()
{
	vector<int> vec = { 2,1,6,5,3,5,2,7 };
	Maopao_sort(vec);
	for (auto it :vec)
	{
		cout << it << endl;
	}
}

桶排序

#include<iostream>
#include<vector>
#include<string>
using namespace std;
void Bucket_sort(vector<int>& vec)
{
	int max = vec[0];
	int s = vec.size();
	for (int i = 0; i < s; i++) //最大值
	{
		if (vec[i] > max)
		{
			max = vec[i];
		}
	}
	vector<int> res(max + 1);  //创建
	for (int i = 0; i < vec.size(); i++)  //记录
	{
		res[vec[i]]++;
		
	}
	//还原
	int Index = 0;
	for (int i = 0; i < res.size(); i++)
	{
		while (res[i] > 0)
		{
			vec[Index++] = i;
			res[i]--;
		}
		
	}


}
int main()
{
	vector<int> vec = { 2,1,6,5,3,5,2,7 };
	Bucket_sort(vec);
	for (auto it :vec)
	{
		cout << it << endl;
	}

}

选择排序:

#include<iostream>
#include<vector>
#include<string>
using namespace std;
vector<int> Sort(vector<int>& nums)
{
	int s = nums.size();
	for (int i = 0; i < s ; i++)
	{
		int MaxIndex = 0;
		int j = 1;
		for (;  j< s - i; j++)
		{
			if (nums[MaxIndex] < nums[j])
			{
				MaxIndex = j;
			}
		}
		swap(nums[MaxIndex], nums[j - 1]);
	}
	return nums;
}
int main()
{
	vector<int> vec = { 1,8,9,5,4,1,3 };
	/*vector<int> chars;*/
	vector<int> chars = Sort(vec);
	for (auto it : chars)
	{
		cout << it << endl;
	}
	return 0;
}

堆排序

void Adjust(vector<int> &vec,int start,int end)
{
	int father = start;
	int child = father * 2 + 1;
	while (child + 1 <= end)
	{
		if(child <= end && vec[child] < vec[child + 1])
		{
			child++;
		}
		if (vec[father] < vec[child])
		{
			swap(vec[father], vec[child]);
			father = child;
			child = father * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void Tree::Heap_Sort(vector<int>& vec)
{
	int n = vec.size();
	for (int i = n / 2 + 1; i >= 0;i--)
	{
		Adjust(vec, i, n - 1);
	}
	for (int i = n - 1; i >= 0; i--)
	{
		swap(vec[0], vec[i]);
		Adjust(vec, 0, i - 1);
	}

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值