04737C++程序设计

04737C++程序设计
一、1.1C++语言的发展简史:程序语言分为低级语言、中级语言、高级语言、机器语言和汇编语言;
       1.2C++语言特点:C语言是C++语言的前身;C++尽量兼容C语言,加入了面向对象的特征,引进了类和对象的概念
       1.2.1基本的输入/输出:
功能 C语言中使用函数 C++语言中提供类 C++类中对象 运算符
键盘输入 scanf() 输入流类istream  cin  >>
屏幕输出 printf() 输出流类ostream  cout  <<
程序一:#include<iostream>                                  //输入输出流,头文件建立连接
              #include<string>                                       //文件包含:嵌入;#为编译指令
              using namespace std;                              //使用标准命名空间 ;
              int main()                                                  //有且仅有一个主函数;有int后面有return 若是void最后没有return
              {
                 int onelnt1,onelnt2;
                 char strArray[20];
                 string str;
                 double oneDouble;
                 char oneChar='a';
                 cout<<"输入两个整型值,一个字符,一个字符串和一个浮点值,";   
                 cout<<"以空格、Tab键或(Enter)键分隔:"<<endl;
                 cin>>onelnt1>>onelnt2>>oneChar>>strArray>>oneDouble;                       ///输入
                 str=strArray;
                 cout<<"输入的数据是:"<<endl;                                                                   ///输出
                 cout<<“两个整型是:\t\t”<<onelnt1<<"和:\t"<<onelnt2<<endl                    //endl是换行,\t是tab键
                        <<"字符是:\t\t"<<oneChar"\n"
                        <<"浮点型是:\t\t"<<oneDouble<<endl;
                   return0;
                 }
1.2.2头文件和命名空间
iostream是C++的调准输入/输出流,每条#include指令仅可以包含一个头文件,如果需要包含多个头文件,则需要使用多条#include指令,嵌入指令
I.标准输入输出流:<iostream>
强制类型转换:

 

 如:7/2=3;2+3.3=5.3;7/2.0=3.5

1.2.4函数参数的默认值
程序二、#include<iostream>             
                using namespace std;             
                void func(int a=11,int b=22,int c=33)                                                //为参数a,b,c分别设置了默认值11,22与33
              {cout<<“a=”<<a<<”,b="<<b<<",c="<<c<<endl;
               }
              int main()
               {
                    func();                                             //调用时缺少了3个实参,将使用定义中的3个参数默认值                                        ///a=11,b=22,c=33
                    func(55);                                         //调用时缺少了后2个实参,将使用定义中的后2个参数默认值                            ///a=55,b=22,c=33
                    func(77,99);                                      //调用时缺少了最后1个实参,将使用定义中的最后1个参数默认值                 // ///a=77,b=99,c=33
                    func(8,88,888);                                    //调用时实参完备,将不使用定义中的任何参数默认值                               ///a=8,b=88,c=888
                    return0;
                }
 注意:提供默认值时必须按从右至左的顺序提供,调用函数时,主调函数的实参与被调函数的形参按从左至右的顺序进行匹配对应。
1.2.5引用和函数参数的传递
定义:如果给某个变量起了别名(不需要给它另开辟内存单元),相当于变量和这个引用都对应到同一地址
程序三、
              #include<iostream>
              usingnamespacestd;
              int main()
                    {
                     int oneInt=1;
                     int&ref=oneInt;                                                                         //ref是oneInt的引用(别名),ref等价于oneInt
                     const int&refc=oneInt;                                                               //定义常引用,加const意思是只允许赋值一次,后面不允许再次直接修改,可以间接修改
                     ref=2;                                                                                          //修改ref也即修改了oneInt
                     cout<<"oneInt="<<oneInt<<”,"<<"ref="<<ref<<endl;                    //输出oneInt=2,ref=2
                     cout<<"refc="<<refc<<endl;                                                          //输出refc=2
                     oneInt=3;                                                                                     //修改oneInt也即修改了ref
                     cout<<"ref=”<<ref<<endl;                                                              //输出ref=3
                     cout<<"refc=”<<refc<<endl;                                                            //输出refc=3,修改了onelnf,间接修改了refc
                     int&ref2=ref;                                                                               //ref2和ref都是oneInt的引用
                     cout<<"ref2="<<ref2<<endl                                                                //输出ref2=3
                     //refc=5;                                                                            //错误,不能直接使用常引用对所引用的变量进行修改
                     return0;
                     }
 程序四:
              #include<iostream>
              using namespace std;
              void SwapValue(int a,int b)
              {
                int tmp;
                tmp=a;
                a=b;
                b=tmp;
                cout<<"在SwapVa1ue( )函数中:\t\ta="<<a<<",b="<<b<<endl;                //a=20,b=10        
                 }
               void SwapRef(int & a,int & b)                                                                   //a、b值互换
               {
                  int tmp;
                  tmp=a;
                  a=b;
                  b=tmp;
                 cout<<"在SwapRef()函数中:\t\ta="<<a<<",b="<<b<<endl;       //a=20  b=10  此时主函数的a和b和此函数的a和b使用的同一个空间
                }
              int main()
                          {
                      int a=10,b=20;
                     cout<<"数据交换前:\t\ta="<<a<<",b="<<b<<endl<<endl;                                            //a=10,b=20
                     SwapValue(a,b);cout<<"调用SwapVal()后:\t\ta="<<a<<",b="<<b<<endl<<endl;        //a=10,b=20      主函数中的a和b都是单独的命名空间,
                     a=10;b=20;
                     SwapRef(a,b);
                      cout<<"调用SwapRef()后:\t\ta="<<a<<",b="<<b<<endl<<endl;                              //a=20,b=10      以上面的函数用一个空间
                       return 0;
                            }
程序五*、
          #include<iostream>
          using namespace std;
          int oneX=10;
          int oneY=20;
          int & refVa1ue(int & x)                                                  ///这里的X与oneX是同一个空间
           {
                return x;
              }
         int main()
             {
              refVa1ue(oneX)=30;                                                    //返回值是引用,可以作为左值使用,这里的oneX与X是同一个空间
              cout<<"oneX="<<oneX<<endl;                                    //输出oneX=30
              refVa1ue(oneY)=40;                                                     //返回值是引用的函数调用表达式,可以作为左值使用
              cout<<"oneY="<<oneY<<endl;                                     //输出oneY=40
               return 0;
                  }
1.2.6 const(常量)与指针共同使用
例子:const char * const p="ABCD";    //const char  表示禁止修改内容,const p 禁止修改指向   const修饰紧挨着的
牢记:const修饰其左侧的内容;如果const是本行的第一个标识符,则它修饰其右侧的内容。
1.2.7 内联函数
         1.内联函数应该定义在前,调用在后,定义时只需在函数头返回值类型的前面加上关键字inline。----类似于整段代码复制一遍到当前需要执行的地方
         2.内联函数主要应用于代码量少的函数,频繁调用----------- 以空间节省时间
         3.如果函数体中有循环语句和switch语句则通常不定义为内联函数。
1.2.8 函数的重载
定义:所谓函数重载,是指在程序的同一范围内声明几个功能类似的同名函数。
实现函数的重载必须满足下列条件之一
        1.参数表中对应的参数类型不同。
        2.参数表中参数个数不同。
注意:1.所谓函数重载,是指在程序的同一范围内声明几个功能类似的同名函数。
           2.仅仅是返回值类型不同,则这两个函数不是重载的
           3.采用引用参数也不能区分函数
           4.避免函数调用时产生二义性,
            如int Sum(int a,intb,intc=0);  
               int Sum(int a,intb);
            则函数调用语句:Sum(1,2);不知道调用哪一个
程序六、
                     #include<iostream>
                     using namespace std;
                      int abs(int n)
                      {
                         return(n<0?-n:n);      //求绝对值判断
                       }
                    float abs(float f)
                      {
                      if(f<0) f=-f;return f;        //求绝对值
                         }
                       double abs(double d)
                      {
                         if(d<0) return -d;
                             return d;
                        }
                  void main()
                     {
                    int a=-6;cout<<abs(a)<<endl;      ////因为是int类型直接调用 abs函数
                     }
 
 1.2.9指针和动态内存分配
例子:int a=100,*p=&a;   指针p指向整型变量a,p中保存a的地址,而不是值100。p指向的地址中的值是100
例子2:
           int * pArray;                            //指向数组的指针
           int i = 5;
          pArray = new int [i*20];           //分配了100个元素的整型数组
          pArray[0] = 20;                        //数组的第一个值
          pArray [99] = 30;                    //数组的最后一个值
          delete [ ] pArray                      //释放数组
 
C++提供了delete运算符,用来释放动态分配的内存空间。
例子三:
int oneInt=6;
int * p=&ontInt;
cout<<*p<<endl;       
delete p;                   //出错,p是引用,不是动态分配的   --指针p指向的空间并不是使用new运算符分配的,所以不能使用delete释放。而指针q指向的空间是使用new分配的,所以使用完毕,通过delete进行释放。
int * q=new int;
    *q=8;
cout<<*q<<endl;
delete q;                  //正确,q指向动态分配的空间
1.2.10 使用string对象处理字符串:#include<string>
string str1;                                  //声明string对象str1,值为空
string city="Beijing";                   //声明string对象city,并使用字符串常量进行初始化
char name[ ]="C++程序";      string s1=name;           //使用字符数组对string变量进行初始化。
string citys[ ]={"Beijing","Shanghai","Tianjin","Chongqing"};     cout<<citys[1]<<endl;          ///输出Shanghai,数组下标从0开始----声明一个string对象数组
 程序七:
#include<iostream>
#include<string>
using namespace std;
void main()
{
string s1, s2;
s1="C++程序";
s2=s1;
string s3;
cout<<"s3= "<<s3<<endl;                                  //输出s3=
s3=s1+s2;
cout<<s1+s2<<endl;                                         //输出C++程序C++程序
cout<<"s3="<<s3<<endl;                                  //输出s3=C++程序C++程序
s3+="de";
cout<<"s3="<<s3<<endl;                                 //输出s3=C++程序C++程序de
bool b=s1<s3;                                                  //b为true
cout<<"bool="<<b<<endl;                               //输出bool=1
char c=s1[2];                                                   //c为'+',下标从0开始计算
cout<<"c="<<c<<endl;                                    //输出c=+
cout<<s1[2]<<endl;                                         //输出+
char arrstr[]="Hello";
s3=s1+arrstr;
cout<<s3<<endl;                                           //输出C++程序Hello
}
 1.3C++语言的程序结构
1.C++程序以.cpp作为文件扩展名
2.有且仅有一个主函数main()
3.程序从主函数main()的开始处执行,主函数可以在任何地方出现,按照其控制结构,一直执行到结束。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5.1 类的继承与派生
“继承”就是支持代码复用的机制之一。派生类派生自基类,或继承于基类,也可以说基类派生了派生类。派生类可以再作为基类派生新的派生类,
5.1.1 继承的概念
除构造函数和析构函数外,基类的所有成员自动成为派生类的成员,包括基类的成员变量和成员函数。可以重新定义或修改基类中已有的成员,包括可以改变基类中成员的访问权限。
5.1.2 派生类的定义与大小
派生类可以改变基类中成员的访问权限
class emptyClass{};               //空基类
class  subempty Class:public emptyClass{};     //派生类
类的大小:派生类对象占用的存储空间大小,等于基类成员变量占用的存储空间大小加上派生类对象自身成员变量占用的存储空间大小。对象占用的存储空间包含对象中各成员变量占用的存储空间。-----可以使用sizeof()函数计算对象占用的字节数。
5.1.3 继承关系的特殊性 
如果基类有友元类或友元函数,则其派生类不会因继承关系而也有此友元类或友元函数。如果基类是某类的友元,则这种友元关系是被继承的。即被派生类继承过来的成员函数,如果原来是某类的友元函数,那么它作为派生类的成员函数仍然是某类的友元函数。总之,基类的友元不一定是派生类的友元;基类的成员函数是某类的友元函数,则其作为派生类继承的成员函数仍是某类的友元函数。
特殊性:
程序一:
#include<iostream>
using namespace std;
class another;                      //前向引用声明
class Base                           //基类
{
private:
float x;
public:
void print(const another &K);
};
class Derived:publicBase                //派生类
{
private:
float y;
};
class another                            //其他类
{
private:
int aaa;
public:
another()
{
aaa=100;
}
friend void Base::print(const another &K);                       //基类的成员函数声明为本类的友元
void Base::print(constanother&K)
{
cout<<"Base:"<<K.aaa<<endl;                                        //可以访问私有成员变量
}
int main()
{
Base a;
Derived d;
another ano;                                                                //aaa初始化为100
a.print(ano);                                                                //输出为:Base:100   访问的是基类base类的print 因为声明了友元函数所以值为100
d.print(ano);                                                               //输出为:Base:100     访问的是派生类Derived 的print 由于继承了基类
return0;
}
程序二:
#include<iostream>
using name space std;
class Base                                           //基类
{
private:
floatx;
public:
static int staV;
Base()
{staV++;}
};
int Base::staV=0;                  静态成员变量赋初值,必须在类体外,这里基类和派生类所共享
class Derived:public Base           //派生类
{
private:
float y;
public:
Derived()
{staV++;}
};
int main()
{
Base a;
cout<<a.staV<<endl;              //输出1
Derived d;
cout<<d.staV<<endl;                 //输出3,调用了基类的构造函数,调用自己类的构造函数
return 0;
}
5.1.4 有继承关系的类之间的访问
基类对象名.基类私有成员函数(实参)
或是“基类对象名.基类私有成员变量”,
或是“基类名::基类私有成员”  如CDobj.CB::showa(),强制调用基类的
5.1.5 protected访问范围说明符
类成员可以使用protected访问范围说明符进行修饰,基类中的保护成员可以在派生类的成员函数中被访问。在基类中,一般都将需要隐藏的成员说明为保护成员而非私有成员。将基类中成员变量的访问方式修改为protected后,在派生类中可以直接访问。----在私有和公有之间,家族内继承可以访问
5.1.6 多重继承
C++允许从多个类派生一个类,也可以从一个基类派生一个派生类的情况,一个派生类至少一个基类活多个基类
派生类继承了基类名1、基类名2、……、基类名n的所有成员变量和成员函数(除了构造函数机构函数之外)
程序三:
#include<iostream>
using name space std;
class CB1
{
public:
int a;                                      //重名
CB1(int x)
{a=x;}
void showa()                         //重名
{cout<<"ClassCB1==>a="<<a<<endl;}
};
class CB2
{
public:
int a;                                    //与上面重名
CB2 (int x)
{a=x;}
void showa()                       //与上面重名
{cout<<"ClassCB2==>a="<<a<<endl;}
}
class CD:public CB1,public CB2               //多重继承,两个基类
{
public:int a;                                              //与两个基类成员变量a重名
CD(int x,int y,int z):CB1(x) ,CB2(y)
{a=z;}
void showa()                                            //与两个基类成员函数showa()重名
{cout<<“Class CD==>a=”<<a<<endl;}
void print3a()
{cout<<"a="<<a<<endl;
cout<<"CB1::a="<<CB1::a<<endl;                
cout<<"CB2::a="<<CB2::a<<endl;
}
};
int main()
{
CB1 CB1obj(11);
CB1obj.showa();
CD CDobj(101,202,909);
CDobj.showa();                               //调用派生类的showa()
CDobj.CB1::showa();                     //调用基类的showa()注意格式
cout<<"CDobj.a="<<CDobj.a<<endl;                 //访问派生类成员a
cout<<"CDobj.CB2::a="<<CDobj.CB2::a<<endl;
}
5.2 访问控制
继承方式说明符可以是public(公有继承)、private(私有继承)或protected(保护继承)
5.2.1 公有继承

 

 5.2.2 类型兼容规则(前提是公有派生)

例子:

class B{…}

class D : public B{…}

B b1,*pb1;

D d1;

1)派生类的对象可以赋值给基类对象。    基类=派生类    b1=d1;

2)派生类对象可以用来初始化基类引用。  B &rb=d1;

3)派生类对象的地址可以赋值给基类指针,即派生类的指针可以赋值给基类的指针。pb1=&d1;

程序四:
#include<iostream>
using name spacestd;
class A
{
int an;
public:
A(){}
A(int n)
{an=n;}
};
classB:publicA                 //公有派生
{
int bn;
public:
B(int n):A(2*n)                      ///派生类给基类赋值
{bn=n;}
  };
int main()
{
Aa(10);
Bb(20);
a=b;                    //派生类对象赋值给基类对象
return0;
}
5.2.3  私有继承

 

5.2.4 保护继承

保护继承中,基类的公有成员和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可以直接访问。这样,派生类的其他成员可以直接访问从基类继承来的公有和保护成员,但在类外通过派生类的对象无法直接访问它们。 
5.3 派生类的构造函数和析构函数(基类的析构、构造函数是不能够继承的)
在执行一个派生类的构造函数之前,总是先执行基类的构造函数。
 5.3.1 构造函数和析构函数
派生类构造函数执行的一般次序如下:
1)调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左向右)。---超过一个基类时
2)对派生类新增的成员变量初始化,调用顺序按照它们在类中声明的顺序。
3)执行派生类的构造函数体中的内容。
4)一般先调用基类的在调用派生类的额构造函数
程序五:
#include<iostream>
using name space std;
class Base
{
private:
int Y;
public:
Base(int y=0)                                          //y=10
{Y=y;
cout<<"Base("<<y<<")"<<endl;
}
~Base()
{cout<<"~Base()"<<endl;}
void print()
{cout<<Y<<"";}
};
class Derived:public Base
{
private:
int Z;
public:
Derived(int y,int z):Base(y)               //形参y=10 z=20
{Z=z;
cout<<"Derived("<<y<<","<<z<<")"<<endl;
}
~Derived()
{cout<<"~Derived()"<<endl;
}
void print()
{Base::print();cout<<Z<<endl;}
};
int main()
{
Derived d(10,20);               //实参     Base(10) 和Derived(10,20)   两个值   因为派生类的构造函数
d.print();                             //    10和20两个值构造函数        //~Derived()和~Base()进行析构
return0;                     
}
5.3.2 复制构造函数
程序六:
#include<iostream>
using name space std;
class CBase
{
public:
CBase(){}
CBase(CBase&c)
{cout<<"CBase::复制构造函数"<<endl;}
CBase&operator=(constCBase&b)
{cout<<"CBase::operator="<<endl;
return*this;
}
};
class CDerived:
public CBase
{
public:
CDerived()
{cout<<"CDerived::复制构造函数"<<endl;}
};
int main()
{
CDerived d1,d2;                                                     //d1时:先基类构造再派生类构造函数  结果为CDerived::复制构造函数;d2时同理结果为CDerived::复制构造函数
CDerivedd3(d1);                                                    //d3初始化过程中会调用类CBase的复制构造函数,    结果为CBase::复制构造函数
d2=d1;                                                                    //会调用类CBase重载的"="运算符       结果为CBase::operator=
return0;
}
5.3.3 多重继承的构造函数与析构函数
要先执行两个基类的构造函数,执行次序依定义派生类DerivedClass时所列基类的次序而定。
例子:classDerivedClass:publicBaseClass1,publicBaseClass2
           所以,先执行基类BaseClass1的构造函数,再执行基类BaSeClass2的构造函数,然后执行派生类DerivedClass的构造函数。析构反之
5.4 类之间的关系
继承关系:也称为“is a”关系或“是”关系。
组合关系:也称为“has a”关系或“有”关系,表现为封闭类,即一个类以另一个类的对象作为成员变量。如class和student类
5.4.2  封闭类的派生
5.4.3  互包含关系的类

 

5.5 多层次的派生

当生成派生类的对象时,会从最顶层的基类开始逐层往下执行所有基类的构造函数,最后执行派生类自身的构造函数;当派生类对象消亡时,会先执行自身的析构函数,然后自底向上依次执行各个基类的析构函数。

类A派生类B,类B可以再派生类C,类C又能够派生类D,以此类推。继承具有传递性D里面有A的类的

5.6 基类与派生类指针的相互转换

 可以使用基类指针指向派生类对象,也可以将派生类的指针直接赋值给基类指针。只能访问从基类继承这一部分

 程序七:
#include<iostream>
using name space std;
class CBase
{
protected:
intn;
public:
CBase(int i):n(i){}
void print()
{cout<<"CBase:n="<<n<<endl;}
};
class CDerived:public CBase
{
public:
int v;
CDerived(inti):CBase(i),v(2*i){}
void Func(){};
void print()
{
cout<<"CDerived:n="<<n<<endl;
cout<<"CDerived:v="<<v<<endl;}
};
int main()
{
CDerived objDerived(3);
CBase objBase(5);
CBase*pBase=&objDerived;                                          //使用基类指针指向派生类对象
CDerived*pDerived;
pDerived=&objDerived;
cout<<"使用派生类指针调用函数"<<endl;
pDerived->print();                                                          //调用的是派生类中的函数
pBase=pDerived;                                                           //基类指针=派生类指针,正确
cout<<"使用基类指针调用函数"<<endl;
pBase->print();                                                              
//pBase->Func();                                                          //错误,访问不到派生类
//pDerived=pBase;                                                        //错误,强制类型转换
pDerived=(CDerived*)pBase;                                          //指针强制类型转换
cout<<"使用派生类指针调用函数"<<endl;                                
pDerived->print();                                                           //调用的是派生类中的函数
return0;
}

第六章    多态与虚函数
6.1 多态的基本概念
多态分为编译时多态和运行时多态。
不同的对象调用相同的函数
6.1.1 多态
在类之间满足赋值兼容的前提下,实现动态绑定必须满足以下两个条件:
1)必须声明虚函数。
2)通过基类类型的引用或者指针调用虚函数。
6.1.2 虚函数
函数声明时前面加了virtual关键字的成员函数。virtual关键字只在类定义中的成员函数声明处使用,不能在类外部写成员函数体时使用。静态成员函数不能是虚函数。包含虚函数的类称为“多态类”。
如果还想调用基类的函数,只需在调用函数时,在前面加上基类名及作用域限定符即可。
格式:virtual 函数返回值类型 函数名(形参表);
关于虚函数,有以下几点需要注意:
1)虽然将虚函数声明为内联函数不会引起错误,但因为内联函数是在编译阶段进行静态处理的,而对虚函数的调用是动态绑定的,所以虚函数一般不声明为内联函数。
2)派生类重写基类的虚函数实现多态,要求函数名、参数列表及返回值类型要完全相同。
3)基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
4)只有类的非静态成员函数才能定义为虚函数,静态成员函数和友元函数不能定义为虚函数。
5)如果虚函数的定义是在类体外,则只需在声明函数时添加virtual关键字,定义时不加virtual关键字。
6)构造函数不能定义为虚函数。最好也不要将operator=定义为虚函数,因为使用时容易混淆。
7)不要在构造函数和析构函数中调用虚函数。在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为。
8)最好将基类的析构函数声明为虚函数。
6.1.3 通过基类指针实现多态
静态(等号左边)动态(等号右边)程序一:
#include<iostream>
using namespace std;
class A
{
public:
virtual void Print()                                                         //虚函数
{cout<<"A::Print"<<endl;}
};
class B:public A                                                             //公有继承
{
public:
virtual void Print()                                                         //虚函数,基类声明为虚函数了,下面的派生类同名函数不声明也为虚函数动态(等号右边);如若基类的同名函数没有声明虚函数,派生类都为虚函数,调用同类函数时候,都输出的是基类的(静态(等号左边)
{cout<<"B::Print"<<endl;}
};
class D:public A                                                             //公有继承
{
public:
virtual void Print()                                                           //虚函数
{cout<<"D::Print"<<endl;}
};
class E:public B                                                              //公有继承
{
public:
virtual void Print()                                                            //虚函数
{cout<<"E::Print"<<endl;}
};
int main()
{
A a;
B b;
D d;
E e;
A*pa=&a;                                                                        //基类pa指指针向基类对象
aB*pb=&b;                                                                     //派生类指针pa指向派生类对象b
pa->Print();                                                                     //多态,目前指向基类对象,调用Print(),输出A::Print
pa=pb;                                                                          //派生类指针赋给基类指针,pa指向派生类对象b
pa->Print();                                                                   //多态,目前指向派生对象,调用b.Print(),由于a是虚函数,看等号右边,输出B::Print
pa=&d;                                                                         //基类指针pa指向基类对象d
pa->Print();                                                                  //多态,目前指向派生类对象,由于a是虚函数,看等号右边,调用d.Print(),输出D::Print
pa=&e;                                                                          //基类指针pa指向基类对象e
pa->Print();                                                                    //多态,目前指向派生类对象,同上,调用e.Print(),输出E::Print
return 0;
}
6.1.4 通过基类引用实现多态
#include<iostream>
using namespace std;
class A                                                    //基类
{
public:
virtual void Print()
{cout<<"A::Print"<<endl;}
};
class B:public A                                      //公有派生
{
public:
virtual void Print()                                       //虚函数
{cout<<"B::Print"<<endl;}
};
void PrintInfo(A &r)                                         ///r=a    r=b
{
r.Print();                                                        //多态,使用基类引用调用哪个Print()取决于r引用了哪个类的对象
}
int main()
{
A a;
B b;
PrintInfo(a);                                                  //使用基类对象,调用基类中的函数,输出A::Print,同程序一
PrintInfo(b);                                                  //使用基类对象,调用派生类中的函数,输出B::Print
return 0;
}
6.1.5 多态的实现原理
派生类对象占用的存储空间大小,等于基类成员变量占用的存储空间大小加上派生类对象自身成员变量占用的存储空间大小。
6.2 多态实例
6.3 多态的使用
在普通成员函数(静态成员函数、构造函数和析构函数除外)中调用其他虚成员函数也是允许的,并且是多态的。
6.4 虚析构函数
格式:virtual〜类名();
虚析构函数没有返回值类型,没有参数,所以它的格式非常简单。
使用虚析构函数的目的是为了在对象消亡时实现多态。
#include<iostream>
using namespace std;
class ABase
{
public:
ABase()
{cout<<“ABase构造函数"<<endl;
}
virtual~ABase()
{cout<<"ABase::析构函数"<<endl;}
};
class Derived:publicABase
{
public:int w,h;                                               //两个成员Derived()
{cout<<"Derived构造函数"<<endl;
w=4;h=7;
}
~Derived()
{cout<<"Derived::析构函数"<<endl;}
};
int main()
{
ABase *p=new Derived();                           //使用基类指针指向new创建的派生类对象,     基类指针=派生类对象     ABase构造函数    Derived构造函数
delete p;                       ///Derived::析构函数    ABase::析构函数  
return 0;
}
6.5 纯虚构函数和抽象类
格式:virtual函数类型函数名(参数表)=0;
纯虚函数是声明在基类中的虚函数,没有具体的定义,
6.5.2 抽象类
包含纯虚函数的类称为抽象类。抽象类的派生类中,如果没有给出全部纯虚函数的定义,则派生类继续是抽象类。直到派生类中给出全部纯虚函数定义后,它才不再是抽象类,也才能实例化一个对象。虽然不能创建抽象类的对象,但可以定义抽象类的指针和引用。
纯虚函数不同于函数体为空的虚函数,它们的不同之处如下:
1)纯虚函数没有函数体,而空的虚函数的函数体为空。
2)纯虚函数所在的类是抽象类,不能直接进行实例化;而空的虚函数所在的类是可以实例化的。
它们共同的特点是:
纯虚函数与函数体为空的虚函数都可以派生出新的类,然后在新类中给出虚函数的实现,而且这种新的实现具有多态特征。
 
 
 
 

 

  

 

posted @   良善伯住阳台  阅读(123)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
点击右上角即可分享
微信分享提示