浏览 3460 次
锁定老帖子 主题:C++多态与类型转换
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-05-25
string name; name.size(); name.c_str(); 因此这里的name就是一个对象. name[下标],这样写不表示它是一个数组. char s[100] = "Hello";//应该是存在了栈空间里 char* p = "Hello";//字符串存在常量区里,栈里只保存了一个地址 输入输出时会对字符串进行特殊的处理. string不是C风格的字符串,是c++风格的,在c++里它会记录它的长度. 在C里我们用strlen(地址),而在c++里我们用obj.size(); 在c++里我们用strlen(obj)是会出错的. ---------------------------------- c++风格: obj = "Hello"; obj[2] = '\0'; obj.size(); //返回5,注意这一点 可以直接输入输出,输入时不用考虑长度,自动会处理长度问题. getline(cin,obj); obj += 附加串 //不用考虑空间问题 obj1 >(<,==,>=,<=,!=) obj2 //直接比,结果为true,false obj.c_str() ---> const char * -------------------------------- C风格字符串 : strcpy(地址,串) 也可以直接输入输出,在输入时,必须要考虑数组长度的问题,避免越界 cin.getline(地址,长度); strcat(旧串,附加串) //注意长度 strcmp(串1,串2)//结果正,负,0 ================================================= 常量函数 在函数后加const 无名对象:没有名字的对象,起到一个类型转换的作用.会的语句执行 完后立即释放. 单重继承:class Child:public Parent{};来自父类的私有成员不能 在子类中直接访问.子类还可以扩展自已的东西,从而拥有比父类更丰富的内容 .在创建时,会自动去调用父类的构造函数,如果要传参数,利用初始化列表传过 去.一个函数的形参是可以有默认值的,在声明的时候就要指定默认值,而不是 在定义时指定.初始化列表写在构造函数的函数头与函数体之间,因此必须在定 义时才能有的,声明不能有.声明写默认值,主义时写初始化列表. 覆盖: 虚继承:是对菱形继承关系,virtual关键字的使用 //关于继承的例子程序,显示代分数 #include <iostream> using namespace std; class Fract{ int n; int d; public: Fract():n(0),d(1){} Fract(int a, int b ):n(a),d(b){ reduce(); } void reduce(){ if(d<0 ){d=-d;n=-n;} if(d==0){cout << "ERROR" << endl;d=1;} int absn = (n<0?-n:n); for(int i=d;i>1;i--){ if(absn%i == 0 && d%i==0 ){ n /= i;d/= i; break; } } } void show(){ cout << n << '/' << d << endl; } double value(){ return (double)n/d; } }; class Dai : public Fract{ int i; public : Dai():i(0){} Dai( int ai, int an, int ad ) : i(ai),Fract(an,ad){} void show(){ cout << i << '(' ; Fract::show(); cout << ')'; } double value(){ return i+Fract::value(); } }; int main() { Dai d1; Dai d2(2,12,16); d1.show(); d2.show(); } 在Linux环境下用g++ fraction.cc进行编译连接,a.out运行可看到结果. ================================================= * 多态 polymorphism 父类对象 人类 子类对像 司机类(小张) 学生类(小王同学) 子类对象总可以看成父类对象. 多态的本质 1,c++允许把派生类对象的地址赋给基类的指针 3,用基类的指针调用任何方法,c++都能找到相应的派生类的方法. 通过指针访问某个成员函数的时候,根据对象的真实类型执行子类中 相应的函数 多态的优势:统一管理,统一操作接口 有virtual关键字的函数称为虚函数.在声明定义分开时,声明时写. 多态的三个要素: 1,继承关系 2,调用的是虚函数 3,访问到的是对象自已(用批针或引用),不能是另一个对象. //例子 #include <iostream> using namespace std; #include <string> class Animal{ string name; public: virtual void eat()=0;//一定不会被执行,纯虚函数 virtual void sleep(){ cout << "动物休息" << endl; } virtual void shout(){ cout << "动物叫" << endl; } };//注意分号 class Cat : public Animal{ public: virtual /* virtual 可写可不写 */ void eat(){ cout << "猫写猫粮" << endl; } void sleep(){ cout << "猫睡觉" << endl; } void shout(){ cout << "猫喵喵叫" << endl; } };//注意分号 class Dog : public Animal{ public : void eat(){ cout << "狗吃骨头" << endl; } void sleep(){ cout << "狗在睡觉" << endl; } void shout(){ cout << "我叫旺财" << endl; } };//分号不能少 class JiaFei : public Cat{ public : void eat(){ cout << "加非猫爱吃意大利面" << endl; } void sleep(){ cout << "加非猫睡在沙发上" << endl; } void shout(){ cout << "加非猫说下午好" << endl; } }; // 分号 class Player{ string name; public : Player( string n ) : name(n){} void play( Animal* p/*指针*/ ){ p->eat(); p->sleep(); p->shout(); } void play( Animal& p /*引用*/){ p.eat(); p.sleep(); p.shout(); } }; // 分号 int main() { //cout << sizeof(Animal) << endl; //输出8 Cat c; Dog d; JiaFei j; Player p1( "小小" ); Player p2( "蔡依林"); p1.play(&c); p2.play(&d); p2.play(&j); } 对有虚函数的类,编译器会申请一片空间,形成一个虚函数表,把所有 的虚函数地址存在里面.所以多态是会有代价的. * 抽象类 * 纯虚函数 有纯虚函数的类叫做抽象类.对于抽象类没必要写函数体,因为如果写 了会占用空间. 纯虚函数一定不会被调用.所以抽象类不允许创建对象. 只能用它来指向子类对象或是引用子类对象. 这样就保证了一定不会执行纯虚函数. * 强制类型转换 (类型) 数据 类型 (数据) 不会改变源数据,根据类型产生新的数据,而++ -- = 可改变源数据. 1,静态转换,static_cast xxx_cast<类型>(数据) 数值类型之间,有类型的指针与 void* 之间转换. 无名类型支持的转换. static_cast<A>(100) (A)100 2,const_cast 把常量转换成变量. const X data; const_cast<X>(data) //会重新产生一个变量 const_cast<X&>(data)=1; //就是对当前常量进行处理 volatile 不稳定的变量,基本不用 auto 自动,默认的,无需人为的去加 register 寄存器, register int n;//将n存在寄存器里,效率较高 reinterpret_cast 重新解释内存,这也是最危险的转换,可以说是高手的游戏, 一般人请不要用.因为什么都可以转.一般用在不同类型指针之间转换. reinterpret_cast<X*>(p) //这样用 3,动态类型转换 dynamic_cast 向上转换是安全的,向下转换就不一定了. dynamic_cast<子类*>(父类地址) 失败 NULL //例子 #include <iostream> using namespace std; class A{ const int data; public: A(){} A(int d) : data(d){} void show() const{ cout << "data=" << data << endl; } virtual void set(int d ){ const_cast<int &>(data) = d; } ///////////这样可以修改常量 }; class B : public A{ public : void sayhello(){ cout << "welcome" << endl; } }; int main() { static_cast<A>(188).show(); A obj(200); obj.show(); obj.set(259); obj.show(); int i = 1; typedef unsigned char UC; UC *p = NULL; p = reinterpret_cast<UC*>(&i); for(int j=0; j<4; j++) cout <<(int)*( p++) << ' '; cout << endl; A oa; B ob; A* pa = &oa; B* pb = NULL; pb = dynamic_cast<B*>(pa); //pb=(B*)pa;这样转是不安全的. //dynamic_cast如果转换不了,会返 回一个NULL cout << "pb = " << pb << endl; } ---------------------------- * 虚函数的使用 构造函数不能是虚函数:因为我们是想用多态,根据我们的情况去调子 类,而构造函数会首先调,不是主要调的,是自动调的,因此不能是虚函数 如果类中有任何一个成员函数是虚函数,那么析构函数应为虚函数.因 为我们想它会自动的去调相应的析构函数. 如果一个类肯定被用作其他派生类的基类,尽可能使用虚函数.甚至可 写成纯虚函数; 用=0;来代替函数体. //例子程序 class A{ public : A(){cout << "'A()" << endl;} ~A(){cout << "'~A()" << endl;} }; class B : public A { public : B(){cout << "'B()" << endl;} ~B(){cout << "'~B()" << endl;} }; int main() { A* p = new B; delete p; // 此句会导致调用析构函数 } ================================================= ================================================= * 友员 * 静态成员 - 静态数据成员 - 静态函数成员 友员提供外面的函数或类直接访问某个类中私有成员的桥梁. 友员也可理解为授权给某一个函数或类. 在类的内部用 friend 声明; 请注意啦 : 友员不是成员 //友员函数 #include <iostream> using namespace std; class A { int data; public : A( int d = 0 ) : data( d ) {} void show( ) { cout << "data=" << data << endl; } friend A add(const A& a1,const A& a2 ); }; A add ( const A& a1, const A& a2 ){ //这样写可形成习惯. int sum = a1.data +a2.data; return A(sum); } int main() { A a1(20), a2(50); add(a1,a2).show(); } //友员类 #include <iostream> using namespace std; class A { int data; public : A( int d = 0 ) : data( d ) {} void show( ) { cout << "data=" << data << endl; } friend A add(const A& a1,const A& a2 ); friend class B; }; class B { public : void twice(A& a){ a.data *= 2; //必须要通过 '对象名.成员' 来访问 } }; A add ( const A& a1, const A& a2 ){ int sum = a1.data + a2.data; return A(sum); } int main() { A oa(50); B ob; ob.twice(oa); oa.show(); } 在实际开发中,凡是在函数中传递对象时,几乎都用引用,只要不改变 此对象,也会加const * 静态数据成员 凡是所有对象共用一份的数据都是要声明为静态数据成员. 静态数据成员又称为类变量. 静态的函数成员叫类方法. 静态成员没必要创建一个对象初始化一次,一次性的初始化,不在构造函数中, 而是在所有的函数之外. 静态数据成员像全局变量一样,在所有函数之外初始化. //例程 #include <iostream> using namespace std; class Sd{ public : string name; static string teacher; static int room; Sd( const string& n) : name(n){ } void show(){ cout << "我是" << name << ",在" << room << "教室听" << teacher << "上课" << endl; } }; string Sd :: teacher = "小陈";// 静态成员要这样初始化 int Sd :: room = 213; int main() { Sd s1("伯乐"); Sd s2("蔡依林"); Sd s3("古天乐"); s1.show(); s2.show(); s3.show(); } * 静态函数成员 静态函数不能去访问非静态的数据成员. 允许但不提倡用 对象.成员 来访问静态成员,类名::静态成员 #include <iostream> using namespace std; class Sd{ public : string name; static string teacher; static int room; #include <iostream> using namespace std; class Sd{ public : string name; static string teacher; static int room; Sd( const string& n) : name(n){ } void show(){ #include <iostream> using namespace std; class Sd{ public : string name; static string teacher; static int room; Sd( const string& n) : name(n){ } void show(){ cout << "我是" << name << ",在" << room << "教室听" << teacher << "上课" << endl; } static void newTeacher( const string& t){ teacher = t; } }; string Sd :: teacher = "小陈";// 静态成员要这样初始化 int Sd :: room = 213; int main() { Sd s1("伯乐"); Sd s2("蔡依林"); Sd s3("古天乐"); s1.show(); s2.show(); s3.show(); Sd::newTeacher("天使"); //通过类名访问静态函数 s1.show(); s2.show(); s3.show(); } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |