多态&指针访问虚函数&不能被继承的类&快速排序&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
        这时begin3位置,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;
}
posted @ 2017-07-02 22:25  乐天的java  阅读(59)  评论(0)    收藏  举报