多态&指针访问虚函数&不能被继承的类&快速排序&N皇后问题&插入排序&堆排序&merge归并排序&栈上生成对象&两个栈实现一个队列
多态
/*
1. 要想实现覆盖(重写)父类必须声明为virtual,子类可以不声明为virtual.-->FunB()
2. 派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
3. 基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
4. 只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
5. 如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。
6. 构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容
易混淆
7. 不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会
出现未定义的行为。
8. 最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构
函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
9. 虚表是所有类对象实例共用的
*/
#include<iostream>
using namespace std;
class CBase
{
public:
virtual void FunA(int a)
{
cout << "CBase::FunA()" << endl;
}
void FunB(int a)
{
cout << "CBase::FunB()" << endl;
}
virtual void FunC(int a1)
{
cout << "CBase::FunC()" << endl;
}
virtual void FunD(int a)
{
cout << "CBase::FunD()" << endl;
}
};
class CDerived :public CBase
{
public:
virtual void FunA(int a)
{
cout << "CDerived::FunA()" << endl;
}
virtual void FunB(int a)
{
cout << "CDerived::FunB()" << endl;
}
void FunC(int a1)
{
cout << "CDerived::FunC()" << endl;
}
virtual void FunD(int a1, int a2)
{
cout << "CDerived::FunD()" << endl;
}
};
int main()
{
CBase* pBase = new CDerived;
pBase->FunA(0); //CDerived::FunA()
pBase->FunB(0);//CBase::FunB()
pBase->FunC(0);//CDerived::FunC()
pBase->FunD(0);//CBase::FunD()
// pBase->FunD(0,0);//编译报错error C2660: “CBase::FunD”: 函数不接受 2 个参数
system("pause");
return 0;
}
/*
1. 覆盖:
1.不同的作用域(分别位于基类和派生类中)
2.函数名称完全相同
3.参数列表完全相同
4.基类函数必须是虚函数
5.返回值类型相同(协变除外)
2. 重载:
1.具有相同的作用域
2.函数名字相同
3.参数类型顺序或数目不同(包括const和非const参数)
4.virtual关键字可有可无
3. 隐藏:
1.派生类的函数与基类的函数同名,但是参数列表有所差异。
此时不论有没有virtual关键字,基类的函数在派生类中都将被隐藏。
2.派生类的函数与基类的函数同名,参数列表也相同,但是基类函数没有
virtual关键字。基类的函数也会被隐藏。
*/
指针访问类里面的虚函数
#include<iostream>
using namespace std;
class A
{
public:
virtual void FunA()
{
cout << "我是A的FunA" << endl;
}
virtual void FunB()
{
cout << "我是A的FunB" << endl;
}
};
class B :public A
{
public:
virtual void FunA()
{
cout << "我是B的A" << endl;
}
virtual void FunB()
{
cout << "我是B的B" << endl;
}
virtual void FunC()
{
cout << "我是B的FunC" << endl;
}
virtual void FunD()
{
cout << "我是B的FunD" << endl;
}
};
/*
定义一个有10个指针的数组tmp,该指针是指向一个整形数: int *tmp[10];
一个指向有10个整形数数组的指针tmp: int (*tmp)[10];
一个指向函数的指针,该函数有一个整型参数并返回一个整型数:int (*fun)(int);
一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数: int(*fun[10])(int);
*/
typedef void(*Fun)(void);
int main()
{
B b;
A a;
Fun pFun[3];
for (int i = 0; i < 4; i++)
{
pFun[i] = (Fun)*((int *)*(int *)&b + i);
/*
*(int*)&b:取到虚函数表的首地址
*(int *)*(int *)&b:取到第一个函数的函数指针
*/
pFun[i]();
}
/*
我是B的A
我是B的B
我是B的FunC
我是B的FunD
请按任意键继续. . .
*/
system("pause");
}
设计一个不能被继承的类
/*
设计一个不能被继承的类
1. 利用友元类可以访问类里所有成员的特性
2. 利用虚基类的构造函数需要由最终的子类调用完成构造的特性
*/
#include<iostream>
using namespace std;
class Grand
{
friend class Parent;
private:
Grand()
{}
~Grand()
{}
};
class Parent:virtual public Grand
{
public:
Parent()
{}
~Parent()
{}
};
/*
设计一个不能被继承的类.cpp(30): error C2248: “Grand::Grand”: 无法访问 private 成员(在“Grand”类中声明)
设计一个不能被继承的类.cpp(14) : 参见“Grand::Grand”的声明
设计一个不能被继承的类.cpp(9) : 参见“Grand”的声明
此诊断出现在编译器生成的函数“Son::Son(void)”中
*/
class Son:public Parent
{
};
int main()
{
Son s;
system("pause");
}
/*
1. 友元不属于任何类,但需要在类的内部声明,声明时需要加friend关键字
2. 友元函数可访问类的私有成员,但不是类的成员函数;
3. 友元函数不能用const修饰;
4. 友元函数可以在类定义的任何地方声明,不受类访问限定
符限制;
5. 函数可以是多个类的友元函数;
6. 友元函数的调用与普通函数的调用和原理相同;
友元类:友元类的所有成员函数都可以是另一个类的友元函数,
都可以访问另一个类中的非公有成员;
*/
/*
友元的优缺点
优点:提高了程序运行效率
缺点:破坏了类的封装性和隐藏性
注意:
友元关系不能继承;
友元关系是单向的,不具有交换性;
友元关系不能传递;
*/
快速排序
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法
#include<iostream>
using namespace std;
//普通法
int partion1(int *arr, int left, int right)
{
//快排若选用左边的为基准值,则指针应当从右边开始走
//反之若选右边的则应当从左边走
/*
如本例,若从左边开始走则
2, 1, 6, 3, 4, 5, 9, 5, 0
2 1 0* 3 4 5 9 5 *6
这时begin在3位置,end也会走到3位置,循环结束
交换arr[left]与arr[begin]
2 1 0 3 4 5 9 5 6
3 1 0 2 4 5 9 5 6
结果不对。
arr[end]>=key
arr[begin] <=key
必须都是大于等于或小于等于号,不能没有等号,
*/
int key = arr[left];
int begin = left;
int end = right-1;
while (begin < end)
{
while (begin<end&&arr[end]>=key)
--end;
while (begin < end&&arr[begin] <=key)
++begin;
if (begin < end)
swap(arr[begin], arr[end]);
}
swap(arr[begin], arr[left]);
return begin;
}
//挖坑法
int partion2(int *arr, int left, int right)
{
int key = arr[left];
int end = right - 1;
int begin = left;
while (begin < end)
{
while (begin<end&&arr[end]>=key)
--end;
arr[begin] = arr[end];
while (begin < end&&arr[begin] <= key)
++begin;
arr[end] = arr[begin];
}
arr[begin] = key;
return begin;
}
//极简双指针法
int partion3(int *arr, int left, int right)
{
int pCur = left;
int pPre = left - 1;
/*
例
2, 1, 6, 3, 4, 5, 9, 5, 4
2 1 3 *6 #4 5 9 5 4
2 1 3 *4 4 5 9 5 6
*/
while (pCur < right)
{
while (arr[pCur] < arr[right - 1] && ++pPre != pCur)
swap(arr[pCur], arr[pPre]);
pCur++;
}
swap(arr[++pPre], arr[right - 1]);
return pPre;
}
void qSort(int *arr,int left, int right)
{
if (right - left < 2)
return;
if (left < right)
{
int Boundary = partion3(arr, left, right);
qSort(arr, left, Boundary);
qSort(arr, Boundary+1, right);
}
}
int main()
{
int arr[] = { 2, 1, 6, 3, 4, 5, 9, 5, 0 };
int len = sizeof(arr) / sizeof(arr[0]);
qSort(arr, 0,len);
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
system("pause");
}
N皇后问题
/*
1.用一个数组保存之前放置皇后的位置,如arr[i]=j,则代表第i行第j列放置了皇后
2. 以行为基准,行每次自增,判断列是否放置合法。
*/
#include<iostream>
using namespace std;
bool isleagle(int *arr, int i, int j)
{
for (int k = 0; k < i; k++)
{
if (j == arr[k] || abs(arr[k] - j) == abs(i - k))
{
return false;
}
}
return true;
}
int getNum(int i, int *arr, int n)
{
if (i == n)
return 1;
int res = 0;
for (int j = 0; j < n; j++)
{
if (isleagle(arr, i, j))
{
arr[i] = j;
//每次递归递归到i==n说明有一条路了,然后返回到上一层,
//上一层的i是n-1这时,j再换一个格子就是j++,然后继续
//递归到n则又有一条路。于是所有路经都计算到了
res += getNum(i + 1, arr, n);
}
}
return res;
}
int gethuanghou(int n)
{
if (n < 1)
return 0;
int *arr = new int[n];
return getNum(0, arr, n);
}
int main()
{
int n;
while (cin >> n)
cout << gethuanghou(n) << endl;
system("pause");
return 0;
}
插入排序
#include<iostream>
using namespace std;
void InsertSort(int *arr, int len)
{
if (len < 2)
return;
for (int i = 0; i < len; i++)
{
int j = i - 1;
int key = arr[i];
while (j>-1 && arr[j]>key)
{
arr[j + 1] = arr[j];
--j;
}
arr[j + 1] = key;
}
}
int main()
{
int arr[] = { 2, 3, 1, 5, 12, 3, 7, 3, 9, 3, 0, 2 };
int len = sizeof(arr) / sizeof(arr[0]);
InsertSort(arr, len);
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
堆排序
#include<iostream>
using namespace std;
class Heap_Sort
{
public:
void Sort(int *arr, int len)
{
_Build_Heap(arr, len);
while (len > 1)
{
swap(arr[0], arr[len - 1]);
len--;
_AdjustDown(arr,0,len);
}
}
private:
void _Build_Heap(int *arr, int len)
{
for (int i = ((len - 2) >> 1); i >= 0; i--)
{
_AdjustDown(arr, i,len);
}
}
void _AdjustDown(int *arr, int root,int size)
{
int parent = root;
int child = 2 * parent + 1;
while (child <size)
{
if (child < size&&arr[child] < arr[child + 1])
child += 1;
if (child < size&&arr[parent] < arr[child])
{
swap(arr[parent], arr[child]);
parent = child;
child = 2 * parent + 1;
}
else
break;
}
}
};
int main()
{
int arr[] = { 5, 3, 1, 7, 4, 0, 6, 8, 4, 7 };
int len = sizeof(arr) / sizeof(arr[0]);
Heap_Sort s;
s.Sort(arr, len);
for (int i = 0; i < len; i++)
cout << arr[i] << " ";
cout << endl;
system("pause");
}
冒泡排序
#include<iostream>
using namespace std;
//注意第二个条件是j<len-i-1
void BubbleSort(int *arr, int len)
{
if (len < 2)
return;
bool flag = true;
for (int i = 0; i < len; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (arr[j] >arr[j + 1])
{
swap(arr[j], arr[j + 1]);
flag = false;
}
}
if (flag)
return;
}
}
int main()
{
int arr[] = { 2, 3, 1, 5, 12, 3, 7, 3, 9, 3, 0, 2 };
int len = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, len);
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
merge归并排序
#include<iostream>
#include<cassert>
using namespace std;
void Merge(int *arr, int left,int mid, int right, int *temp)
{
int begin1 = left;
int end1 = mid;
int begin2 = mid + 1;
int end2 = right;
int idx = left;
while (begin1 <= end1&&begin2<=end2)
{
if (arr[begin1] < arr[begin2])
temp[idx++] = arr[begin1++];
else
temp[idx++] = arr[begin2++];
}
while (begin1<=end1)
temp[idx++] = arr[begin1++];
while (begin2<=end2)
temp[idx++] = arr[begin2++];
for (int i = left; i<=right; i++)
arr[i] = temp[i];
}
void _Merge_Sort(int *arr, int left, int right, int *temp)
{
if (right > left)
{
int mid = left + ((right - left) >> 1);
_Merge_Sort(arr, left, mid, temp);
_Merge_Sort(arr, mid + 1, right, temp);
Merge(arr, left, mid,right, temp);
}
}
void Merge_Sort(int *arr, int left, int right)
{
assert(right - left > 1);
int *temp = new int[right - left + 1];
_Merge_Sort(arr, left, right, temp);
delete[]temp;
}
int main()
{
int arr[] = { 2, 3, 45, 2, 5, 1, 8, 5, 2, 3, 0 };
int len = sizeof(arr) / sizeof(arr[0]);
Merge_Sort(arr, 0, len-1);
for (int i = 0; i < len; i++)
{
cout <<arr[i] << " ";
}
cout << endl;
system("pause");
}
只能在栈上生成对象
/*
只有使用new运算符,对象才会建立在堆上,因此,只要禁用new运算符就可以实现类对象只能建立在栈上。将operator new()设为私有即可。代码如下:
*/
#include<iostream>
using namespace std;
class Base
{
private:
void *operator new(size_t);
void operator delete(void *);
public:
Base()
{}
~Base();
};
int main()
{
Base *ptr = new Base;
}
只能在堆上生成对象
/*
容易想到将构造函数设为私有。在构造函数私有之后,无法在类外部调用构造函数来构造类对象,只能使用new运算符来建立对象。然而,前面已经说过,new运算符的执行过程分为两步,C++提供new运算符的重载,其实是只允许重载operator new()函数,
而operator()函数用于分配内存,无法提供构造功能。因此,这种方法不可以。
当对象建立在栈上面时,是由编译器分配内存空间的,
调用构造函数来构造栈对象。当对象使用完后,编译器会调用析构函数来释放栈对象所占的空间。
编译器管理了对象的整个生命周期。如果编译器无法调用类的析构函数,情况会是怎样的呢?
比如,类的析构函数是私有的,编译器无法调用析构函数来释放内存。
所以,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,
只要是非静态的函数,编译器都会进行检查。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。
因此,将析构函数设为私有,类对象就无法建立在栈上了。代码如下:
class A
{
public :
A(){}
void destory(){ delete this ;}
private :
~A(){}
};
*/
/*
试着使用A a;来建立对象,编译报错,提示析构函数无法访问。这样就只能使用new操作符来建立对象,构造函数是公有的,可以直接调用。类中必须提供一个destory函数,来进行内存空间的释放。类对象使用完成后,必须调用destory函数。
上述方法的一个缺点就是,无法解决继承问题。如果A作为其它类的基类,则析构函数通常要设为virtual,然后在子类重写,以实现多态。因此析构函数不能设为private。还好C++提供了第三种访问控制,protected。将析构函数设为protected可以有效解决这个问题,类外无法访问protected成员,子类则可以访问。
另一个问题是,类的使用很不方便,使用new建立对象,却使用destory函数释放对象,而不是使用delete。(使用delete会报错,因为delete对象的指针,会调用对象的析构函数,而析构函数类外不可访问)这种使用方式比较怪异。为了统一,可以将构造函数设为protected,然后提供一个public的static函数来完成构造,这样不使用new,而是使用一个函数来构造,使用一个函数来析构。代码如下,类似于单例模式:
*/
#include<iostream>
using namespace std;
class Base
{
private:
Base()
{}
~Base()
{}
public:
static Base* getInstance()
{
return new Base;
}
void destory()
{
delete this;
}
};
int main()
{
Base *b = Base::getInstance();
}
两个栈实现一个队列
#include<iostream>
#include<stack>
using namespace std;
template<class T>
class StackQueue
{
public:
void Push(T data)
{
//Spush的入栈就相当于入队
Spush.push(data);
}
void Pop()
{
/*
如果Spop为空,则一直将sPush里面的元素压入Spop中
直到Spush为空,然后Spop弹出,就相当于入队
如果Spop不为空则直接弹出,就相当于出队
*/
if (Spop.empty())
{
while (!Spush.empty())
{
Spop.push(Spush.top());
Spush.pop();
}
}
Spop.pop();
}
T Front()
{
if (Spop.empty())
{
while (!Spush.empty())
{
Spop.push(Spush.top());
Spush.pop();
}
}
return Spop.top();
}
bool empty()
{
return Spop.empty() && Spush.empty();
}
private:
stack<T> Spush;
stack<T> Spop;
};
int main()
{
StackQueue<int> q;
q.Push(1);
q.Pop();
q.Push(2);
q.Push(3);
q.Pop();
q.Pop();
cout << q.empty() << endl;
//cout << q.Front() << endl;
system("pause");
return 0;
}
有getMin功能的栈
#include<iostream>
#include<stack>
#include<cstdio>
using namespace std;
/*
注意
1. 在得到Min值时,首先要判断stack是否为空,不为空再getMin
2. Min在pop时要判断pop的元素是否为当前的最小元素
*/
template<class T>
class MinStack
{
public:
T Top()
{
return _stack.top();
}
void Push(T data)
{
_stack.push(data);
if (_Min.empty() || data < _Min.top())
{
_Min.push(data);
}
}
bool Empty()
{
return _stack.empty();
}
void Pop()
{
if (_stack.top() == _Min.top())
_Min.pop();
_stack.pop();
}
T getMin()
{
//注意在getMin是要判断栈是否为空
if (_stack.empty())
{
return INT_MAX;
}
return _Min.top();
}
private:
stack<T> _stack;
stack<T> _Min;
};
int main()
{
/*MinStack<int> s;
s.Push(1);
s.Push(4);
s.Push(5);
s.Push(0);
cout << s.getMin() << endl;
cout << INT_MAX << endl;
system("pause");*/
MinStack<int> st;
char s[100];
int op;
while (scanf("%s", s) == 1 && strcmp(s, "END") != 0)
{
if (strcmp(s, "PUSH") == 0)
{
scanf("%d", &op);
st.Push (op);
printf("push=%d\n", op);
}
else if (strcmp(s, "POP") == 0)
{
op = st.Top();
st.Pop();
printf("pop=%d\n", op);
}
else if (strcmp(s, "TOP") == 0)
{
printf("top=%d\n", st.Top());
}
else if (strcmp(s, "MIN") == 0)
{
printf("min=%d\n", st.getMin());
}
}
return 0;
}

浙公网安备 33010602011771号