Ⅰ类与对象①
1.定义
#include <iostream> using namespace std; const int N = 100; class CPPStack { private: //数据为私有 char s[N]; //栈的内容保存在s中 int tp; //栈顶指示器,栈空为-1 public: //成员函数放在类中,会很臃肿,但代码效率高 void init(); //声明和定义分开,更清晰,一目了然 bool isEmpty(); bool isFull(); void push(char c); char pop(); char top(); }; void CPPStack::init() //指明对象即可 { tp = -1; } bool CPPStack::isEmpty() { return (tp==-1); } bool CPPStack::isFull() { return (tp==(N-1)); } void CPPStack::push(char c) { tp++; s[tp] = c; } char CPPStack::pop() { int oldtp = tp; tp--; return s[oldtp]; } char CPPStack::top() { return s[tp]; } int main() { char str[] = {"0123456789"}; CPPStack s; cout << str << endl; s.init(); int i = 0; while(str[i] && !s.isFull()) s.push(str[i++]); while(!s.isEmpty()) cout << s.pop(); cout << endl; }
1.1 从C结构体到C++新结构体
#include <iostream> #include <cstring> using namespace std; struct StuNode //C里不能有函数 { int ID; char name[32]; char gender[8]; int age; }; //const使Set能够读取,却不改变其值 //Set在外面,要有指针才能接触到struct void Set(struct StuNode *s, int id, const char *sn, const char *sg, int a) { s->ID = id; strcpy(s->name, sn); //赋值时麻烦 strcpy(s->gender, sg); s->age = a; } void Output(struct StuNode s) { cout << s.ID << "," << s.name << "," << s.gender << "," << s.age << endl; } int main() { struct StuNode myNode; //定义有3个 Set(&myNode, 101, "Tom", "male", 35); Output(myNode); return 0; }
#include <iostream> #include <string> using namespace std; struct StuNode //可以拥有函数小可爱啦 { int ID; string name; string gender; int age; void Set(int id, const string &sn, const string &sg, int a) { ID = id; //啧啧啧,看看人家这赋值,多方便 name = sn; gender = sg; age = a; } void Output() { cout << ID << "," << name << "," << gender << "," << age << endl; } }; int main() { StuNode myNode; //瘦身成功! myNode.Set(101, "Tom", "male", 35); myNode.Output(); return 0; }
1.2 从面向过程到面向对象
#include <iostream> using namespace std; const int N = 100; struct CStack { char s[N]; //栈的内容保存在s中 int tp; //栈顶指示器,栈空为-1 }; void init(CStack* stk) //每个函数都要放到外面去 { stk->tp = -1; //还要传递参数 } bool isEmpty(CStack stk) //要更改值的时候,用指针 { return (stk.tp==-1); //不更改时,就用变量就好啦 } bool isFull(CStack stk) { return (stk.tp==(N-1)); } void push(CStack* stk, char c) { stk->tp++; stk->s[stk->tp] = c; //真的很麻烦欸 } char pop(CStack* stk) { int oldtp = stk->tp; stk->tp--; return (stk->s[oldtp]); } char top(CStack stk) { return (stk.s[stk.tp]); } int main() { char str[] = {"0123456789"}; struct CStack s; cout << str << endl; init(&s); int i = 0; while(str[i] && !isFull(s)) push(&s, str[i++]); while(!isEmpty(s)) cout << pop(&s); cout << endl; }
#include <iostream> using namespace std; const int N = 100; struct CPPStack { char s[N]; //栈的内容保存在s中 int tp; //栈顶指示器,栈空为-1 void init() //函数放到结构体里,就不用在每次的形参 { 中说自己的对象是谁了 tp = -1; } bool isEmpty() { return (tp==-1); } bool isFull() { return (tp==(N-1)); } void push(char c) { tp++; s[tp] = c; } char pop() { int oldtp = tp; tp--; return s[oldtp]; } char top() { return s[tp]; } }; int main() { char str[] = {"0123456789"}; CPPStack s; cout << str << endl; s.init(); //使用函数的时候,要说明是谁的函数 int i = 0; while(str[i] && !s.isFull()) s.push(str[i++]); while(!s.isEmpty()) cout << s.pop(); cout << endl; }
1.3 类的定义
1.3.1 引入
■ 树(类)有好多种,比如槐树、柳树、桂花树(对象),但这些树有一些共同的特征,比如都有树干树枝树叶、有纹理、有年龄(属性),都可以生长、随风飘摇(行为&操作)
类(class)是具有相同属性和行为特征的一组对象的集合,是对他的对象的一种抽象描述
■ int a;中,int:数据类型,表示整数,不分配存储空间,a是变量,是整数中的一个,分配存储空间
类是一种复杂的数据类型,可以用类类型定义一个对象:类 对象;
消息:就是命令,让对象干什么事
1.3.2 定义
默认情况下,新结构体中的所有成员均是公有的而类中的所有成员均是私有的。
1.4 对象的建立与使用
1.5 成员的存取控制
2.构造函数与析构函数
之前设计的栈,由于空间是定好了的,你永远不知道会冒出来一个数据符不符合,所以我们可以动态地分配一下,当空间小的时候,就加个倍
#include <iostream> //改变的地方用红色标记吧 using namespace std; class CPPStackExt { private: char *s; //栈的内容保存在s中 int tp; //栈顶指示器,栈空为-1 int size; //增加了一个变量表示容量 public: void init(); bool isEmpty(); bool isFull(); void push(char c); char pop(); char top(); }; void CPPStackExt::init() { tp = -1; size = 5; s = new char[size]; } bool CPPStackExt::isEmpty() { return (tp==-1); } bool CPPStackExt::isFull() { return (tp==(size-1)); } void CPPStackExt::push(char c) { if (isFull()) { char *p = new char[2*size]; //增加一个新指针,指向一个新区域 for (int i=0; i<size; i++) //把以前的先拷贝到新区域里 p[i] = s[i]; delete []s; //删除以前的痕迹 s = p; size = 2*size; //扩成两倍 } tp++; s[tp] = c; } char CPPStackExt::pop() { int oldtp = tp; tp--; return s[oldtp]; } char CPPStackExt::top() { return s[tp]; } int main(){ CPPStackExt s; char str[]={"012345678"}; cout << str << endl; //此时输出0123456789 s.init(); int i=0; while(str[i]) s.push(str[i++]); while(!s.isEmpty()) cout<<s.pop(); //此时输出9876543210 cout<<endl; return 0; }
就像是倒水一样,5L的杯子里装满了水,但是现在变成10L水了,要先把5L倒出来,放到另一个10L的杯子里才行。
但是这样会出现问题,如果让你一直到10L水,你必须用更多的杯子来装水,这样会浪费大量的空间。这就跟所有的动态分配一样,用完之后要释放空间
//增加一个释放空间的函数 void CPPStackExt::release() { if(s!=NULL) { delete []s; s = NULL; } }
2.1 构造函数
#include <iostream> //改变的地方用红色标记吧 using namespace std; class CPPStackExt { private: char *s; //栈的内容保存在s中 int tp; //栈顶指示器,栈空为-1 int size; //增加了一个变量表示容量 public: CPPStackExt() //构造函数①——无形参,用之前的 { // 函数()时调用 init(); cout << "111" << endl; } CPPStackExt(int initSize) //构造函数②——带默认形参,新写的 { //函数(3)时调用 tp = -1; size = initSize; s = new char[size]; cout << "222" << endl; } CPPStackExt(const CPPStackExt &scopy) //拷贝构造函数,新写的 { //函数先()再(s)时调用 tp = scopy.tp; size = scopy.size; s = scopy.s; cout << "333" << endl; } void init(); //不能没有啊,不然找不到 bool isEmpty(); bool isFull(); void push(char c); char pop(); char top(); }; void CPPStackExt::init() { tp = -1; size = 5; s = new char[size]; } bool CPPStackExt::isEmpty() { return (tp==-1); } bool CPPStackExt::isFull() { return (tp==(size-1)); } void CPPStackExt::push(char c) { if (isFull()) { char *p = new char[2*size]; //增加一个新指针,指向一个新区域 for (int i=0; i<size; i++) //把以前的先拷贝到新区域里 p[i] = s[i]; delete []s; //删除以前的痕迹 s = p; size = 2*size; //扩成两倍 } tp++; s[tp] = c; } char CPPStackExt::pop() { int oldtp = tp; tp--; return s[oldtp]; } char CPPStackExt::top() { return s[tp]; } int main(){ char str[]={"0123456789"}; cout << str << endl; //此时输出0123456789 // s.init(); //函数①自动调用,不需要了 CPPStackExt s(3); int i=0; while(str[i]) s.push(str[i++]); CPPStackExt s1(s); while(!s.isEmpty()) cout<<s.pop(); //此时输出9876543210 cout<<endl; return 0; }
2.1.1 性质
①系统自动调用
②函数名与类名相同
③无返回值
④属于public共有的
⑤构造函数可以重载,名字可以一样
2.1.2 成员初始化列表
就是在{ }外面写
类中若存在引用、const数据成员,不能使用赋值的显式初始化方式
2.2 析构函数
2.2.1 作用
对象消失时的清理工作(如释放内存单元)
2.2.2 性质
①没有返回值
②没有任何参数,不能重载
③在对象消失时由系统自动调用
~CPPStackExt()
{
release();
cout << "析构" << endl;
}
2.2.3 构造函数与析构函数的异同