C++基础之类
2018-01-03 21:45
260 查看
特别注明:本文章主要参考《C++语言程序设计教程》(第3版)沈显君 杨进才 张勇 编著 *****************************************初步认识面向对象编程的代码风格 #include<iostream> using namespace std; class my_clock{ private: int h,m,s; public: //下面的函数在类内部定义,为内联(inline)函数;也可放在类外定义,往下看哦 void set_time(int hh,int mm,int ss){ h=(hh>=0 && hh<=23)?hh:0; m=(mm>=0 && mm<=59)?mm:0; s=(ss>=0 && ss<=59)?ss:0; } void show_time(){ cout<<h<<":"<<m<<":"<<s<<endl; } }; int main() { my_clock clock1; clock1.show_time(); clock1.set_time(15,43,6); clock1.show_time(); return 0; } //成员函数定义在类的外面,上述代码可以如下修改 #include<iostream> using namespace std; class my_clock{ private: int h,m,s; public: //下面的函数在类内部定义,为内联(inline)函数;也可放在类外定义,往下看哦 void set_time(int hh,int mm,int ss); void show_time(); }; int main() { my_clock clock1; clock1.show_time();//不初始化,则系统提供任意值 clock1.set_time(15,43,6); clock1.show_time(); return 0; } //::这是作用域辨别符 void my_clock::set_time(int hh,int mm,int ss){ h=(hh>=0 && hh<=23)?hh:0; m=(mm>=0 && mm<=59)?mm:0; s=(ss>=0 && ss<=59)?ss:0; } void my_clock::show_time(){ cout<<h<<":"<<m<<":"<<s<<endl; } ************************************************注意成员函数的内存分配 C++编译器创建对象的时候,只为数据成员初始化值,不为各对象的成员函数分配内存, 而是共享类的成员函数定义。 ************************************************存取控制属性 public:该类成员及所有对象 protected:该类及其子类成员 private:该类的成员 ********************************对象初始化之构造函数,对象消失时之析构函数 #include<iostream> using namespace std; class my_clock{ private: int h,m,s; public: //下面的函数在类内部定义,为内联(inline)函数;也可放在类外定义,往下看哦 void set_time(int hh,int mm,int ss); void show_time(); //下面两个是构造函数,一般用于初始化对象,创建对象时编译器自动调用 my_clock(int hh,int mm,int ss){ h=hh; m=mm; s=ss; cout<<"constructed"<<endl; } my_clock(){cout<<"constructed"<<endl;} //下面是析构函数,对象消失时系统自动调用,无参数,无返回值,一个类只有一个 ~my_clock(){ cout<<"destructed"<<endl; } }; int main() { my_clock clock1; my_clock clock2(1,2,3); my_clock clock3=my_clock(6,6,6); clock1.show_time();//不初始化,则系统提供任意值 clock1.set_time(15,43,6); clock1.show_time(); clock2.show_time(); clock3.show_time(); return 0; } //::这是作用域辨别符 void my_clock::set_time(int hh,int mm,int ss){ h=(hh>=0 && hh<=23)?hh:0; m=(mm>=0 && mm<=59)?mm:0; s=(ss>=0 && ss<=59)?ss:0; } void my_clock::show_time(){ cout<<h<<":"<<m<<":"<<s<<endl; } ************************************************拷贝构造函数 #include<iostream> using namespace std; class my_clock{ private: int h,m,s; public: my_clock(int hh=0,int mm=0,int ss=0){ h=hh,m=mm,s=ss; cout<<"constructed: "<<h<<":"<<m<<":"<<s<<endl; } ~my_clock(){ cout<<"destructed: "<<h<<":"<<m<<":"<<s<<endl; } //下面是拷贝构造函数,注意形参必须是引用,否则递归调用拷贝构造函数造成死循环 my_clock(my_clock &p){ cout<<"copied and constructed: "<<h<<":"<<m<<":"<<s<<endl; h=p.h; m=p.m; s=p.s; } }; my_clock fun(my_clock c){return c;} int main() { my_clock c1(1,0,0); my_clock c2(2,0,0); my_clock c3(3,0,0); fun(c2); my_clock c4; c4=c2; return 0; } 运行结果: constructed: 1:0:0 constructed: 2:0:0 constructed: 3:0:0 copied and constructed: 1086697686:-2:1970498266 copied and constructed: 6881016:6881228:1970522304 destructed: 2:0:0 destructed: 2:0:0 constructed: 0:0:0 destructed: 2:0:0 destructed: 3:0:0 destructed: 2:0:0 destructed: 1:0:0 从上面结果的第4行和第5行可以看出,在调用函数fun时,一是建立形参临时对象, 二是函数返回时,建立临时对象,都调用了拷贝构造函数。由于拷贝构造函数中的 cout<<"copied and constructed: "<<h<<":"<<m<<":"<<s<<endl;这条语句写在 了h=p.h;m=p.m;s=p.s;这几条语句的前面,所以在输出这句话的时候,建立的临时 对象尚未赋值,值是任意的。从这一点我们可以清楚看出,这里建立临时对象的时候 没有调用构造函数(如果调用构造函数,则输出值应该是0,0,0,不应该是任意值), 这一点帮助我们知道了构造函数和拷贝构造函数的区别 几点注意: 1)拷贝构造函数只是在用一个已存在的对象去初始化新建立的对象时调用。 2)用常量初始化已经建立的对象时,调用构造函数,不调用拷贝构造函数。 3)建立对象时,构造函数和拷贝构造函数有且只有一个被调用。 4)当对象作为函数的返回值时需要调用拷贝构造函数,此时C++将从堆中建立一个临时 对象,将函数返回的对象复制给该临时对象,并把临时对象的地址存储到寄存器里,从而 由该临时对象完成函数返回值的传递。 ************************************************浅拷贝和深拷贝之字符串类 #include<iostream> #include<cstring> using namespace std; class String{ private: char *str; int len; public: void show_str(){ cout<<"string: "<<str<<", length:"<<len<<endl; } String(){ len=0; str=NULL; } String(const char *p){ len=strlen(p); str=new char[len+1]; strcpy(str,p); } ~String(){ if(str!=NULL){ delete[] str; str=NULL; } } }; int main() { char *str="hello1"; String s1(str); String s2("hello2"); s1.show_str(); s2.show_str(); //下面用s6直接复制给s7,在使用类默认的拷贝构造函数,属于浅拷贝 //没有新开辟内存空间,s6,s7公用相同的内存,但是在程序执行结束的 //时候,系统调用析构函数,把同一段内存释放两次,这是不允许的, //应该要报错的,而codeblocks就比较神奇,不报错 String s6("abcdefg"); String s7=s6; s7.show_str(); return 0; } //添加深拷贝构造函数,如下代码 #include<iostream> #include<cstring> using namespace std; class String{ private: char *str; int len; public: void show_str(){ cout<<"string: "<<str<<", length:"<<len<<endl; } String(){ len=0; str=NULL; } String(const char *p){ len=strlen(p); str=new char[len+1]; strcpy(str,p); } ~String(){ if(str!=NULL){ delete[] str; str=NULL; } } //深拷贝 String(String &p){ len=p.len; if(len!=0){ str=new char[len+1]; strcpy(str,p.str); } } }; int main() { char *str="hello1"; String s1(str); String s2("hello2"); s1.show_str(); s2.show_str(); String s6("abc"); String s7=s6; s7.show_str(); return 0; } ****************************************************************对象指针与对象引用 和一般数据类型的指针、引用类似 ****************************************************************动态对象 动态对象就是动态分配内存建立对象,程序员可以随时建立,随时销毁。非动态建立的对象,则何时 建立,何时销毁是系统说的算的。 ****************************************************************this指针 this指针是指向当前对象的指针,是系统预定义好的指针 ****************************************************************组合对象实例 /* 本程序是计算火车旅途时间,并设定火车旅途时间不超过24小时 火车旅途开始时间设置一个类,火车旅途结束时间设置一个类。 此两个类组合在一起成为旅途时间类,具体看下面代码。 */ #include<iostream> using namespace std; class clock{ private: int h,m,s; public: clock(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } void show_time(){ cout<<h<<":"<<m<<":"<<s<<endl; } void set_time(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } int get_h(){return h;} int get_m(){return m;} int get_s(){return s;} }; class train_trip{ private: char *train_no; clock start_time; clock end_time; public: train_trip(char *train_no,clock st,clock en){ this->train_no=train_no; start_time=st; end_time=en; } clock trip_time(){ int th,tm,ts; int carry=0;//借位 clock tmp; (ts=end_time.get_s()-start_time.get_s())>=0?carry=0:(ts+=60,carry=1); (tm=end_time.get_m()-start_time.get_m()-carry)>=0?carry=0:(tm+=60,carry=1); (th=end_time.get_h()-start_time.get_h()-carry)>0?carry=0:th+=24;//假定火车旅途时间不超过24小时 tmp.set_time(th,tm,ts); return tmp; } }; int main() { clock c1(8,10,10),c2(6,1,2); clock c3; train_trip trip("T66",c1,c2); //train_trip trip("T66",8,10,10,6,1,2); c3=trip.trip_time(); c3.show_time(); return 0; } //下面改变了组合对象初始化方式,即改变了组合对象类的构造函数,如下 #include<iostream> using namespace std; class clock{ private: int h,m,s; public: clock(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } void show_time(){ cout<<h<<":"<<m<<":"<<s<<endl; } void set_time(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } int get_h(){return h;} int get_m(){return m;} int get_s(){return s;} }; class train_trip{ private: char *train_no; clock start_time; clock end_time; public: //组合对象初始化可以用如下列表格式,:后的对象顺序随意,成员对象初始化顺序只取决于其在所定义的类中的顺序 train_trip(char *train_no,int sh,int sm,int ss,int eh,int em,int es):end_time(eh,em,es),start_time(sh,sm,ss){ this->train_no=train_no; } clock trip_time(){ int th,tm,ts; int carry=0;//借位 clock tmp; (ts=end_time.get_s()-start_time.get_s())>=0?carry=0:(ts+=60,carry=1); (tm=end_time.get_m()-start_time.get_m()-carry)>=0?carry=0:(tm+=60,carry=1); (th=end_time.get_h()-start_time.get_h()-carry)>0?carry=0:th+=24;//假定火车旅途时间不超过24小时 tmp.set_time(th,tm,ts); return tmp; } }; int main() { clock c3; train_trip trip("T66",8,10,10,6,1,2);//改动处,与组合对象初始化改动一致起来 c3=trip.trip_time(); c3.show_time(); return 0; } //再次改动组合对象初始化的方式,如下 #include<iostream> using namespace std; class clock{ private: int h,m,s; public: clock(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } void show_time(){ cout<<h<<":"<<m<<":"<<s<<endl; } void set_time(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } int get_h(){return h;} int get_m(){return m;} int get_s(){return s;} }; class train_trip{ private: char *train_no; clock start_time; clock end_time; public: //组合对象初始化可以用如下列表格式 train_trip(char *train_no,clock st,clock en):end_time(en),start_time(st){ this->train_no=train_no; } clock trip_time(){ int th,tm,ts; int carry=0;//借位 clock tmp; (ts=end_time.get_s()-start_time.get_s())>=0?carry=0:(ts+=60,carry=1); (tm=end_time.get_m()-start_time.get_m()-carry)>=0?carry=0:(tm+=60,carry=1); (th=end_time.get_h()-start_time.get_h()-carry)>0?carry=0:th+=24;//假定火车旅途时间不超过24小时 tmp.set_time(th,tm,ts); return tmp; } }; int main() { clock c3; train_trip trip("T66",clock(8,10,10),clock(6,1,2));//改动处,与组合对象初始化改动一致起来 c3=trip.trip_time(); c3.show_time(); return 0; } 参照上面的第2个程序,组合对象的构造函数与析构函数调用顺序如下列出: c3.clock(); start_time.clock(); end_time.clock(); trip.train_trip("T66",8,10,10,6,1,2); tmp.clock(); tmp.~clock();//局部函数调用结束 clock();//函数返回一个对象,先建立一个临时对象 ~clock();//函数调用结束,返回对象,建立的临时对象销毁 trip.~clock(); end_time.~clock(); start_time.~clock(); c3.~clock(); *********************************************************************类的静态数据成员与静态成员函数 无论创建多少对象,类中定义的某个静态数据成员只有他自身一个,为所有对象共享,必须类内声明,类外初始化。 具体格式,直接参看下面的程序代码。 静态成员函数为所有对象共享,无论建立多少对象,他本身的数目只有一个。类的静态成员函数可以直接访问类的静态 成员,但不能直接访问非静态成员,若非要访问非静态成员,可以通过参数传递的方式间接通过对象访问。 静态成员无this指针 //本程序是练习使用静态数据成员和静态成员函数来进行对象的计数 #include<iostream> #include<cstring> using namespace std; class student{ private: char *name; int no; static int cnt;//静态数据成员,类内定义 public: static int get_cnt(){return cnt;}//静态成员函数 student(char* ="",int=0); student(student&); ~student(); }; student::student(char *name,int no){ this->name=new char[strlen(name)+1]; strcpy(this->name,name); this->no=no; ++cnt; cout<<"constructed: "<<name<<endl; } student::student(student &p){ name=new char[strlen(p.name)+1]; strcpy(this->name,p.name); no=p.no; ++cnt; cout<<"copied and constructed: "<<p.name<<endl; } student::~student(){ cout<<"destructed: "<<name<<endl; delete[] name; --cnt; } int student::cnt=0;//静态数据成员,类外初始化 int main() { cout<<student::get_cnt()<<endl; student s1("Tome"); cout<<s1.get_cnt()<<endl; student s2(s1); cout<<s1.get_cnt()<<endl; student s3[3]; cout<<student::get_cnt()<<endl; student *s4=new student[2]; cout<<student::get_cnt()<<endl; delete[] s4; cout<<student::get_cnt()<<endl; return 0; } ******************************************************友元 友元不是类的成员,但可以访问类的所有成员。友元分为友元函数和友元类。 A是B的友元类,B是C的友元类,则推不出A是C的友元类,不可传递。 A是B的友元类,很显然推不出B是A的友元类。若声明A是B的友元类,则A首先 必须存在,所以在定义两个类互为友元的时候,应该如下定义: class B;//两个类互为友元类,则必须这样使用 class A{ public: void funA(B b); }; class B{ public: void funB(A,a); }; //友元函数实例如下 #include<iostream> using namespace std; class clock{ private: int h,m,s; public: clock(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } void set_time(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } void show_time(){ cout<<h<<":"<<m<<":"<<s<<endl; } friend clock trip_time(clock st,clock en);//友元的定义可以在类内也可以在类外 //友元就是一个普通的函数,不属于类,只是这个函数可以访问类的所有成员 }; clock trip_time(clock st,clock en){ int th,tm,ts; int carry=0; clock tmp; (ts=en.s-st.s)>=0?carry=0:(ts+=60,carry=1); (tm=en.m-st.m-carry)>=0?carry=0:(tm+=60,carry=1); (th=en.h-st.h-carry)>=0?carry=0:(th+=24,carry=1); tmp.set_time(th,tm,ts); return tmp; } int main() { clock c1(8,10,10),c2(6,1,2); clock c3; c3=trip_time(c1,c2); c3.show_time(); return 0; } //友元类实例如下 #include<iostream> using namespace std; class clock{ private: int h,m,s; public: clock(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } void set_time(int h=0,int m=0,int s=0){ this->h=h,this->m=m,this->s=s; } void show_time(){ cout<<h<<":"<<m<<":"<<s<<endl; } friend class train_trip; }; class train_trip{ private: char *train_no; clock st,en; public: train_trip(char *train_no,clock s,clock e):st(s),en(e){ this->train_no=train_no; } clock trip_time(){ int th,tm,ts; int carry=0; clock tmp; (ts=en.s-st.s)>=0?carry=0:(ts+=60,carry=1); (tm=en.m-st.m-carry)>=0?carry=0:(tm+=60,carry=1); (th=en.h-st.h-carry)>=0?carry=0:th+=24; tmp.set_time(th,tm,ts); return tmp; } }; int main() { clock c1(8,10,10),c2(6,1,2); clock c3; train_trip trip("T66",c1,c2); c3=trip.trip_time(); c3.show_time(); return 0; } *************************************************C++中大量系统函数的形参用了const修饰 传址调用时候,保护实参不被改变,增强程序健壮性,另一方面增强程序的可读性。 *************************************************常对象 常对象的定义格式: const clock c1(2,3,4); clock const c2(5,6,7); 常对象不能被改变,被赋值等,只能访问常成员函数,不能访问非常成员函数。 //先看常数据成员 #include<iostream> using namespace std; class A{ private: const int &r;//常变量可以把const放在int前 int const a;//常变量可以把const放在int后 static const int b; public: //常数据成员的初始化只能通过如下初始化列表进行 A(int i):a(i),r(a){ cout<<"constructed"<<endl; } void show(){ cout<<a<<" "<<r<<" "<<b<<endl; } }; const int A::b=6; int main() { A a(1); a.show(); A b(2); b.show(); return 0; } /* 本程序练习常成员函数,常成员函数不能对数据成员更新, 不能调用没有const修饰的成员函数 */ #include<iostream> using namespace std; class date{ private: int y,m,d; public: date(int y=0,int m=0,int d=0){ this->y=y,this->m=m,this->d=d; } void show(); void show()const;//const可用于重载,常对象调用常成员函数,一般对象调用一般成员函数 }; void date::show(){ cout<<"非常成员函数调用: "; cout<<y<<"-"<<m<<"-"<<d<<endl; } void date::show()const{ cout<<"常成员函数调用: "; cout<<y<<"-"<<m<<"-"<<d<<endl; } int main() { date date1(2017,1,1); const date date2(1999,1,1); date1.show(); date2.show(); return 0; }