学C++也有大半个月了,先来总结下目前为止学了什么.
但也要清楚,在项目中并不去严格区分面向过程或面向对象。程序严格的讲就是一个流程式的操作的,面向对象只不过是让项目的管理更加的方便而已
所以不要区分的太死,一定是面向过程或一定是面向对象.
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#include<cstdlib>
#include <stdio.h>
#include <tchar.h>
#include<cstring>
#include<iostream>
using namespace std;
namespace Function//功能
{
namespace Sort//排序
{
void QuickSort(int arr[], int left, int right);//快排
void HillSort(int arr[], int len);//希尔排序
}
namespace KMP//快速查找字符串
{
void GetNext(char *str, int nextval[]);//跳转数组
int Kmp(char *str1, char *str2);//kmp算法
}
namespace Show//打印函数
{
void Show(int*Arr, int len);
}
}
// TODO: 在此处引用程序需要的其他头文件
// 第二次作业.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
void Function::Sort::QuickSort(int arr[], int left, int right)//快速排序
{
int low = left;
int high = right;
int temp = arr[left];//挖第一个坑
if (left > right)//递归出口
{
return;
}
while (low != high)
{
for (; low<high&&arr[high]>temp; high--){}
if (low < high)
{
arr[low++] = arr[high];//满足条件填左边的坑同时又在右边挖一个坑
}
for (; low < high&&arr[low] < temp; low++){}
if (low < high)
{
arr[high--] = arr[low];//又满足条件填右边的坑同时又给左边挖一个坑
}
}
arr[high] = temp;//当左右指针相同,吧最开始挖出来的东西填在这个坑
QuickSort(arr, left, high - 1);//左半部
QuickSort(arr, high + 1, right);//右半部
}
void Function::Sort::HillSort(int arr[], int len)//希尔排序
{
int gap;
for (gap = len / 2; gap > 0; gap /= 2)//确定步长.分成gap个组,分别进行插入排序
{
for (int i = gap; i < len; ++i)
{
for (int j = i - gap; j >= 0 && arr[j]>arr[j + gap]; j -= gap)//插入排序
{
arr[j] ^= arr[j + gap];
arr[j + gap] ^= arr[j];
arr[j] ^= arr[j + gap];
}
}
}
}
void Function::KMP::GetNext(char *str, int nextval[])//next跳转数组,直接跳过相同的
{
unsigned int i = 0;
int j = -1;
nextval[0] = -1;
while (i < strlen(str))
{
if (j == -1 || str[i] == str[j])
{
i++;
j++;
nextval[i] = j;
}
else
/*j = nextval[j];*/
j = -1;
}
}
int Function::KMP::Kmp(char *str1, char *str2)//kmp算法
{
int i = 0;
int j = 0;
int* nextval = (int*)calloc(100, sizeof(int));
int lenstr1 = strlen(str1);
int lenstr2 = strlen(str2);
GetNext(str2, nextval);
while ((i < lenstr1) && (j < lenstr2))
{
if (j == -1 || str1[i] == str2[j])
{
i++;
j++;
}
else
j = nextval[j];
if (j == lenstr2 || j == lenstr1)
{
free(nextval);
return i - lenstr2;
}
}
free(nextval);
return -1;
}
void Function::Show::Show(int*Arr,int len)//打印函数
{
for (int i = 0; i < len; ++i)
{
cout << Arr[i] << endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int Arr[10] = { 1, 5655, 6565, 56, 56, 55481, 77, 87877, 8, 78 };
int Arr2[10] = { 1, 58, 54, 7, 8, 8, 7887, 88, 8 ,10};
Function::Sort::QuickSort(Arr, 0, 9);
Function::Sort::HillSort(Arr2, 10);
Function::Show::Show(Arr,10);
Function::Show::Show(Arr2,10);
int NEXT[32] = { 0 };
char Name[32] = "jdfhjfkdkfdjfhjdshfjdshjfh";
char name[32] = "djfh";
Function::KMP::GetNext(name, NEXT);
if (Function::KMP::Kmp(Name, name) != -1)
{
cout << "查找成功位置在:"<<Function::KMP::Kmp(Name,name) << endl;
}
system("pause");
return 0;
}
(2)类的定义 关键字 class
class mm//标识符
{
//此处默认私有属性
public://公有属性 公有接口,通过对象可以直接访问
mm(){}//构造函数 用来初始化 (两种出事化方式)数据成员,在构造对象是自动调用
~mm(){}//析构函数 对象死亡时自动调用,此处注意如果类中使用了堆区内存,就需要自己写析构函数,否则有默认析构函数
protected://保护属性 和私有属性一样都不能被对象在类外访问
private://私有属性 同上,与保护类型区别在于继承多态这块
}
这里提一下内联函数,内联函数即inline修饰的函数,在预编译时会将inline修饰的函数直接加入主函数,优点减少时间复杂度避免常用代码重复调用浪费时间,不过最好不要修饰代码量多的函数.否则适得其反,在类中实现函数,即默认为内联函数.
(3)const 修饰的数据成员,成员函数,对象;static 修饰的变量, 函数,以及友元函数
(a)const
类中const 修饰数据成员,必须通过初始化参数列表的方式初始化,且一经初始化不能修改
类中const修饰成员函数时必须加在函数参数列表后进行修饰,原因加在前面会被默认为返回类型,const函数中的this指针所指的数据成员都不能修改(this指针是系统默认指对象的指针)
类外创建对象时用const修饰时,即常量对象,该对象只能访问const修饰的成员函数.
(b)static
类中static修饰变量,该变量存在全局区,不属于任何对象但属于这个类,能被所有对象共享,可以用来计数等.
类中static修饰的函数,该函数也是不属于单个对象属于这个类,该函数只能访问静态数据,不能直接访问类中成员数据,因为没有this指针(只能通过传参对象的引用并要有接口)
类中其他成员函数都可以随意访问静态变量和函数.
(c)友元 关键字friend 友元是一种打破类封装性的工具.
friend修饰的函数称为友元函数,首先它不属于类是个普通函数,外部访问不需要加域名加以限定,友元函数内部通过传参该类对象的引用,能直接访问私有数据而不需要接口函数
注意友元不具有传递性,单向,不能被继承(注意在任意属性下声明都一样).
friend class Sort,即友元类的声明,可以理解为友元类中的所有成员函数都是另一个类的友元函数.
例子:
#include<iostream>
using namespace std;
class A
{
public:
A(int x=0, int y=0) :x(x), y(y){}
friend void show(A & b);
protected:
friend class B;
private:
int x;
int y;
};
void show(A & b)//注意友元函数不属于类不需要void A::show();这样实现
{
cout <<b. x <<b. y << endl;
}
class B
{
public:
B(int d = 0, int f = 0) :d(d), f(f){}
void show(A& a)
{
cout << d << f << endl;
cout << a.x << a.y << endl;
}
protected:
private:
int d;
int f;
};
int main()
{
A d(2,3);
show(d);
B g(5,6);
g.show(d);
system("pause");
return 0;
}
(4)继承 说到底也是为了程序更具条理性,能更好的管理维护,继承即一个类去继承另一个类的所有属性,注意三种属性继承,分别为public,protected,private,哪种属性继承的最高权限就是哪个属性.
下面举个例子:
//这里举个菱形继承结构的例子,多层复杂继承关系,
#include<iostream>
using namespace std;
//class A
//{
//public:
// A(int x=0, int y=0) :x(x), y(y){}
// void show()
// {
// cout << x << y << endl;
// }
//protected:
//private:
// int x;
// int y;
// //占8字节
//};
//class B :public A
//{
//public:
// B(int d = 0, int f = 0) :d(d), f(f){}
// void show()
// {
// cout << d << f << endl;
// }
// int d;
// int f;
// //占16字节
//};
//class D :public A
//{
//public:
// D(int c = 0, int k = 0) :c(c), k(k){}
// void show()
// {
// cout << c << k << endl;
// }
// int c;
// int k;
// //占16字节
//};
//class F :public B, public D
//{
//public:
// F(int i = 0, int o = 0) :i(i), o(o){}
// void show()
// {
// cout << i << o << endl;
// }
// int i;
// int o;
// //占40字节
//};
class A
{
public:
A(int x = 0, int y = 0) :x(x), y(y){}
void show()
{
cout << x << y << endl;
}
protected:
private:
int x;
int y;
//占8字节
};
class B : virtual public A
{
public:
B(int d = 0, int f = 0) :d(d), f(f){}
void show()
{
cout << d << f << endl;
}
int d;
int f;
//占20字节
};
class D : virtual public A
{
public:
D(int c = 0, int k = 0) :c(c), k(k){}
void show()
{
cout << c << k << endl;
}
int c;
int k;
//占20字节
};
class F :public B, public D
{
public:
F(int i = 0, int o = 0) :i(i), o(o){}
void show()
{
cout << i << o << endl;
}
int i;
int o;
//占40字节
};
int main()
{
A d;
B f;
D t;
F a;
cout << sizeof(d) << endl;
cout << sizeof(f) << endl;
cout << sizeof(t) << endl;
cout << sizeof(a) << endl;
/*注意构造派生类F时会构造两次A,加大了浪费时间和空间复杂度,且访问A类属性会产生二义性
避免二义性,一种是通过B和D的域名加以限定,还有就是通过virtual关键字修饰(即虚继承)
总之虚继承就是防止A被继承两次,
这里再说一下继承顺序吧,有点类似递归,有个口诀就是,构造一个类时先判断是否有基类有先构造基类,没有判断有无子对象,有先构造子对象,最后构造本身,构造基类和子对象 时递归这个操作,
比如这个例子:构造顺序为ABADF
这里再提一下虚这个概念,virtual,你可以理解为一个抽象出来的指针,在这种菱形复杂继承时一般都使用虚继承,节约空间.
(在类中只要有virtual该类实例化就会多4字节,且在 类内存的前端)
只要有虚继承,继承顺序:继承链中只要有虚继承,毫无道理的先构造虚基类,然后按之前的构造顺序,如果遇到virtual就通过这个指针去判断是否已经构造如果已经构造就通过 没有构造下面的,即ABDF,减少一次A的构造,这里注意A就不是由BD间接构造的了,而是由派生类F直接构造,所以如果虚基类必须带参构造时,
F也要给A传参(注意虚基类的内存一定是在整个派生类内存的末端,这个可以自己去尝试)*/
system("pause");
return 0;