C++实验2 继承和多态

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档







前言

在此次实验中我将对C++ 类的多继承,虚继承,多态性以及友元函数进行实现练习


提示:以下是本篇文章正文内容,下面案例可供参考





一、实验内容

         一、继承访问权限测试
        1.
设计类A具有public, protected, private等不同属性的成员函数或变量

        2.类B通过public, protected, private等不同方式继承A,在类B的成员函数中测试访问A的成员函数或变量;
        3.在类B中添加public, protected, private等不同属性的成员函数或变量,在外部测试访问B的各个成员函数或变量;

        4.B以private方式继承A,尝试把A中的部分public成员提升为public。
        二、友元类继承测试
        1.设计类A含有私有变量a,在类A中友元给类C;
        2.设计类B继承A,添加私有变量b;在类C中测试访问类B的成员变量a, b;
        3.设计类D继承C,在D的成员函数中测试访问类A的成员变量a,类B的成员变量a, b。
        三、多态性综合运用
        1.一般多态性函数:输入输出参数完全一样,在父类中添加virtual;
        2.特殊多态性函数:输入或输出参数在子类中是父类的指针或基类的引用,在子类中对应的是子类的指针或子类的引用;
        3.析构函数的多态性;(多继承,注意什么情况需要虚继承;)
        4.设计矢量图,运用多继承设计组合图形,要求具备创建不同类型矢量图、选择图形、移动图形、用不同颜色显示图形(表示选中与否),用vector或数组管理图形。
        


二、实验过程





2.1 继承访问权限测试

设计类Person具有public,protected,private三种类型的变量和成员函数,作为父类进行测试不同继承方式下的访问权限

class Person
{
public:                                //共有函数成员
    string m_strName;
    Person();
    Person(string strName,string strSex,int Age);

    void SetSex(string strSex)
    {
        m_strSex = strSex;
    }
    
protected:                             //保护数据成员
    string m_strSex;

private:                               //私有数据成员
    int m_nAge;
};

类B通过public, protected, private等不同方式继承A,在类B的成员函数中测试访问A的成员函数或变量;

2.1.1 以public方式继承类Person

class Student: public Person
{
public:
    void SetName(string strName="")    //隐含的内联函数
    {          
        m_strName = strName;         //student通过public继承Person在类的内部可以访问
       
    }
    void SetSex(string strSex="")  
    {          
        m_strSex = strSex;
    }
    void SetAge(int Age)  
    {          
        //m_nAge = Age;         //public继承方式不能访问private变量
    }
};
void TestPerson()
{
    Person perObj;
    perObj.m_strName = "RenRuijie";
    //perObj.m_strSex = "";            //父类Person的protected属性在外部无法访问
    //perObj.m_nAge = 0;               //父类private属性在外部也无法访问
    Student stuObj;
    stuObj.m_strName = "RenRuijie";
    //stuObj.m_strSex = "man";      //public继承下,父类的protected属性同样无法在外部被子类访问
    //stuObj.m_nAge = 0;             //public继承下,父类的private属性无法在外部被子类访问
}

2.1.2 以protected继承类Person

使用protected来继承父类,原有权限高于protected的变量都会被降成protected

class Worker:protected Person
{
public:
    using Person::m_strName;
    using Person::m_strSex;
    //using Person::m_nAge;             //父类的private属性不能用此方式提升他的访问权限
    void SetName(string strName="")    
    {
        m_strName = strName;         //Worker通过protected继承Person在类的内部可以访问
    }
    void SetSex(string strSex="")
    {          
        m_strSex = strSex;          //父类protected属性用protected继承也可以访问
    }
    void SetAge(int Age)
    {
        //m_nAge = Age;             //protected继承方式不能访问private变量
    }
    string & GetName()
    {
        return m_strName;
    }
};
void TestPerson()
{
    Worker worker;
    worker.SetName("LiSan");
    worker.m_strName=""; 
    worker.m_strSex ="man";
    //worker.m_nAge = 0;
    //protected继承下,父类的所有变量无法在外部被子类访问,
    //通过using语句可以提升原访问权限,使其可以在外部被访问
    //private属性属于私有,用using依旧不可行
}





2.1.2 以private继承类Person

使用private来继承父类,原有权限高于private的变量都会被降成private

//private继承则所有属性变为私有
class Teacher:private Person
{
public:
    using Person::m_strName;        //获得父类成员的方法二
    using Person::m_strSex;
    //继承类没有权限访问基类的private属性,只能访问到public和protected属性
    //using Person::m_nAge;
    string & GetName()              //获得父类成员的方法一
    {
        return m_strName;
    }
    void SetName(string strName="")    
    {
        m_strName = strName;         //Teacher类通过private继承Person在类的内部可以访问
    }
    void SetSex(string strSex="")
    {          
        m_strSex = strSex;          //父类protected属性用private继承也可以访问
    }
    void SetAge(int Age)
    {
        //m_nAge = Age;             //private继承方式不能访问private变量
    }
    
};
void TestPerson()
{
    Teacher teacher;
    //teacher.m_strName = "RenRuijie";
    //用public,protected,private继承不会影响内部访问,只能访问到protected以上属性,private修饰的成员变量只能在他的类的内部使用
    //用protected,private继承的不能被外部访问,可以用using提升访问权限到public
    teacher.SetSex("");               //SetSex会采取就近调用的方式

    teacher.GetName();
    teacher.m_strName = "";
    teacher.m_strSex = "";
    //teacher.m_nAge = 10;
    //若不用using提升访问权限,则父类public,protected,private的变量均无法访问
}

总结:1.类的单继承,class 派生类名: 继承方式 基类名

           2.类的多继承,class 派生类名: 继承方式1   基类名1,继承方式2   基类名2....

           3.继承类只能访问父类protected级以上的变量或成员函数,不能访问private

           4.用public,protected,private继承不会影响内部访问,只能访问到protected以上属性,private

           修饰的成员变量只能在他的类的内部使用

           5.用protected,private继承的不能被外部访问,可以用using提升访问权限到public

           6.改变后该成员在派生类的访问权限由using声明语句之前的派生类访问说明符确定

2.2 友元类继承测试

        将Teacher 类设置为Person类的友元类,对继承权限进行测试

public:                                //公有函数成员
    string m_strName;                  //姓名
    Person();
    ~Person();
    Person(string strName,string strSex,int Age);
    void SetSex(string strSex)
    {
        m_strSex = strSex;
    }
    friend class Teacher;       //将Teacher类设置成Person类的友元类,使其子类所有函数均可访问父类成员
    
    friend istream & operator>>(istream &is,Person &per);
    friend ostream & operator<<(ostream &os,const Person &person);
    bool Create(string strName,string strSex,int Age);

protected:                             //保护数据成员
    string m_strSex;                   //性别

private:                               //私有数据成员
    int m_nAge;                        //年龄
};

2.2.1 子类为基类友元类时继承权限

class Teacher:private Person
{
public:
    //using Person::m_strName;
    //using Person::m_strSex;
    //using Person::m_nAge;
    string &GetName()
    {
        return m_strName;
    }
    void SetSex(string strSex="")
    {
        m_strName="";
        m_strSex=strSex;
        m_nAge=10;           //通过设置友元类,子类可访问父类的私有变量
    }
    //
    int GetAge()
    {
        m_nAge=20;
    }
    
protected:
private:
};
void TestTeacher()
{
    //测试友元类对继承的影响
    Teacher tea;
    //tea.m_strName ="SYP";
    //tea.m_strSex ="man";       
    //设置为友元类时用private继承,同样不能访问父类成员
}

将Teacher类作为Person类的友元类时,用private继承在子类内部可以访问父类所有成员,包括private变量,但是在外部都不能进行访问。

2.2.2 友元类对于基类派生类的影响

class Teacher:private Person
{
public:
    //using Person::m_strName;
    //using Person::m_strSex;
    //using Person::m_nAge;
    Teacher();
    ~Teacher();
   
    string &GetName()
    {
        return m_strName;
    }
    void SetSex(string strSex="")
    {
        m_strName="";
        m_strSex=strSex;
        m_nAge=10;           //通过设置友元类,子类可访问父类的私有变量
    }
    void TestTeacher()
    {
        m_nAge =100;           //Teacher设置为Person的友元类,可以访问基类Person的私有变量
        m_nScore = 100;        //无法执行,无法访问基类Person的其余派生类Student的私有变量
    }
    int GetAge()
    {
        m_nAge=20;
    }
    
protected:
private:
};
class Student: public Person
{
public:
    Student();
    ~Student();
    
    void SetName(string strName="")    //隐含的内联函数
    {
        m_strName = strName;         //student通过public继承Person在类的内部可以访问
    }
    void SetSex(string strSex="")
    {          
        m_strSex = strSex;
    }
    void SetAge(int Age)
    {
        //m_nAge = Age;         //public继承方式不能访问private变量
    }
    inline string GetSex();
protected:
    double m_nNumber;           //学生成绩
private:
    string m_nScore;          //学生学号
};

通过测试发现,基类的友元类在他的内部可以访问基类的私有变量,但是却不能对基类其他派生类产生此种影响。

2.2.3 友元类的派生类对于基类和其派生类的访问权限

class Lecturer:public Teacher
{
public:
    Lecturer();
    ~Lecturer();
    void TestLecturer()
    {
        Student stu;
        m_nAge = 0;        //基类的私有变量无法访问
        stu.m_nAge = 0;
        stu.m_nScore = 0;  //基类派生类的私有变量也无法访问  
    }
protected:
private:
};

2.2.4 友元函数提升某个函数访问权限

class A;
class Person;
class A
{
public:
    void TestFriend(Person &per);
    void TestFriend2(Person &per)
    {
        //per.m_nAge = 10;
    }
};
class Person
{
public:                                //公有函数成员
    string m_strName;                  //姓名
    Person();
    ~Person();
    Person(string strName,string strSex,int Age);
    void SetSex(string strSex)
    {
        m_strSex = strSex;
    }
    friend class Teacher;       //将Teacher类设置成Person类的友元类,使其子类所有函数均可访问父类成员
    friend void A::TestFriend(Person &per);    //友元成员函数,只允许TestFriend()访问私有变量
    //友元成员函数:在类A的内部声明一个属于类B的成员函数,则只有该成员才能访问类A的私有成员
    friend void TestFriendFun(Person &per)  //友元函数(全局),同样可以访问私有变量(第一种实现方式:实现过程在类的内部)
    {
        per.m_nAge =20;
    }
    friend void TestFriendFun(Person &per); //第二种实现方式:实现过程在类的外部
    
    friend istream & operator>>(istream &is,Person &per);
    friend ostream & operator<<(ostream &os,const Person &person);
    bool Create(string strName,string strSex,int Age);

protected:                             //保护数据成员
    string m_strSex;                   //性别

private:                               //私有数据成员
    int m_nAge;                        //年龄
};
/*
void TestFriendFun(Person &per)     //友元函数(全局),同样可以访问私有变量
{
    per.m_nAge =20;
}
*/
void A::TestFriend(Person &per)
{
    per.m_nAge = 0;
}

注:

1.友元函数是在类中用关键字friend修饰的非成员函数。友元函数可以是一个普通的函数,也可以是其他类的成员函数,虽然他不是本类的成员函数,但是在他的函数体中可以通过对象名访问类的私有和保护成员。友元函数是其他类的成员函数,如在类A 的内部声明一个属于类B的成员函数,则该成员函数可以访问类A的私有和保护变量(例:friend void A::TestFriend(Person &per);)。

2.若类A 为类B 的友元类,则A类的所有成员函数都是类B 的友元函数,都可以访问类B的私有和保护成员。

3.友元关系是不具有传递性,类B是类A 的友元,类C是类B 的友元,类C和类A之间如果没有声明,就没有任何友元关系,不能进行数据共享。

4.友元关系是单向的,不具有交换性,如果声明类B是类A的友元,类B的成员函数就可以访问类A的私有和保护数据,但类A的成员函数却不能访问类B的私有、保护数据。

5.友元关系是不被继承的,如果类B是类A的友元,类B的派生类并不会自动称为类A的友元。

2.3 多态性的综合运用

2.3.1 使用virtual修饰函数实现多态性

如果用基类类型的指针指向派生类对象,就可以通过这个指针来访问该对象,问题是访问到的只是从基类继承来的同名成员。解决这一问题的办法是:如果需要通过基类的指针指向派生类的对象,并访问某个与基类同名的成员,那么首先在基类中将这个同名函数说明为虚函数。这样通过基类类型的指针,就可以使属于不同派生类的不同对象产生不同的行为,从而实现了运行过程的多态。

class Person   //A类
{
public:                                //公有函数成员
    string m_strName;                  //姓名
    Person();
    virtual ~Person();
    Person(string strName,string strSex,int Age);
    virtual void SetType();
    void SetSex(string strSex)
    {
        m_strSex = strSex;
    }
   
    friend istream & operator>>(istream &is,Person &per);
    friend ostream & operator<<(ostream &os,const Person &person);
    bool Create(string strName,string strSex,int Age);

protected:                             //保护数据成员
    string m_strSex;                   //性别

private:                               //私有数据成员
    int m_nAge;                        //年龄
};

//类的单继承,class 派生类名: 继承方式 基类名
//继承类只能访问父类protected级以上的变量或成员函数,不能访问private
class Student: public Person   //B类
{
public:
    Student();
    ~Student();
    void SetType();
    void SetName(string strName="")    //隐含的内联函数
    {
        m_strName = strName;         //student通过public继承Person在类的内部可以访问
    }
    void SetSex(string strSex="")
    {          
        m_strSex = strSex;
    }
    void SetAge(int Age)
    {
        //m_nAge = Age;         //public继承方式不能访问private变量
    }
    inline string GetSex();
protected:
    double m_nNumber;           //学生成绩
private:
    string m_nScore;          //学生学号
};
int main(int argc, char *argv[])
{
   
    Person *stu = new Student();    //基类使用派生类的构造器,基类类型的指针指向派生类对象
    stu->SetType();      //使用基类和派生类都实现的setType()函数测试真正调用的是哪一个

    return 0;
}

输出结果为“My class is student!” ,表明调用的是Student类中的setType()函数。说明当使用虚函数继承机制的时候,会调用派生类中的函数,而不是基类。

2.3.2 特殊多态性

输入或输出参数在子类中是父类的指针或基类的引用,在子类中对应的是子类的指针或子类的引用;假如传入的是派生类,同样希望函数保留派生类的多态,即根据传入参数类型调用相应函数。

定义一个全局函数SetType,在声明中我们定义传入参数类型为Person,对传入不同的参数进行测试。

void TestType(Person *p)    //传入的是基类的指针
{
    p->SetType();
}
int main(int argc, char *argv[])
{
   
    Person *stu = new Student();    //基类使用派生类的构造器,基类类型的指针指向派生类对象
    stu->SetType();      //使用基类和派生类都实现的setType()函数测试真正调用的是哪一个
   
    Person p1;
    Student s1;
    TestType(&p1);
    TestType(&s1);
    return 0;
}

或 

void TestType(Person &p)    //传入的是基类的引用
{
    p.SetType();
}

int main(int argc, char *argv[])
{
 
    Person *stu = new Student();    //基类使用派生类的构造器,基类类型的指针指向派生类对象
    stu->SetType();      //使用基类和派生类都实现的setType()函数测试真正调用的是哪一个
    
    Person p1;
    Student s1;
    TestType(p1);
    TestType(s1);
    return 0;
}

运行结果均为:

这样通过传入参数的不同调用相应的方法,实现了参数传值的多态性。

2.3.3 析构函数的多态性

假如我们用基类的指针创建一个子类的对象,那么在delete的时候执行的就是基类的析构函数,子类虽然继承了基类,但是却没有重写基类的析构函数,则无法析构子类的指针,可能会造成内存泄漏。

Person::~Person()
{
    cout<<"delete Person!"<<endl;
}

Student::~Student()
{
    cout<<"delete Student!"<<endl;
}
int main(int argc, char *argv[])
{
    
    Person *stu = new Student();    //基类使用派生类的构造器,基类类型的指针指向派生类对象
    stu->SetType();      //使用基类和派生类都实现的setType()函数测试真正调用的是哪一个
    delete  stu;
    
    return 0;
}

运行结果:

 可以看出只执行了基类的析构函数,解决方法是将基类的析构函数用virtual修饰,设置为虚函数就可以实现析构函数的多态性。

class Person   //A类
{
public:                                //公有函数成员
    string m_strName;                  //姓名
    Person();
    virtual ~Person();
    Person(string strName,string strSex,int Age);
    virtual void SetType();

    friend istream & operator>>(istream &is,Person &per);
    friend ostream & operator<<(ostream &os,const Person &person);
    bool Create(string strName,string strSex,int Age);

protected:                             //保护数据成员
    string m_strSex;                   //性别

private:                               //私有数据成员
    int m_nAge;                        //年龄
};

运行结果:

用virtual修饰后,delete子类时就会先执行子类的析构函数,再执行父类的析构函数。

 2.3.4 纯虚函数

在一个类中,若在其成员函数后面加上 =0,则该函数为纯虚函数,如下:

class Person   //A类
{
public:                                //公有函数成员
    string m_strName;                  //姓名
    Person();
    virtual ~Person();
    Person(string strName,string strSex,int Age);
    virtual void SetType();
    virtual void SetWork() =0;     //纯虚函数
    void SetSex(string strSex)
    {
        m_strSex = strSex;
    }
}

纯虚函数的特点:

1.纯虚函数只有函数声明,没有函数体,不具备函数的功能,不能被调用;

2.纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据所需对其进行定义。

3.含有纯虚函数的类称为抽象类(虚基类),它不能生成对象。同时基类中对纯虚函数没有定义,在派生类中必须加以实现。

4.与纯虚函数函数不同的是虚函数为了重载和多态的需要,在基类中是有定义的,即便定义为空,所以子类中可以重写也可以不重写基类的函数,而纯虚函数在子类中必须加以实现。

2.4 构建矢量图

运用多继承设计组合图形,要求具备创建不同类型矢量图、选择图形、移动图形、用不同颜色显示图形(表示选中与否),用vector或数组管理图形,实现了shapeDll类,CShape类,CPoint类,CRect类,CRectPoint类,以及图形管理类ShapeManager类

2.4.1 Shapedll.h

#include <string>
#include <math.h>
#include <QPainter>
#include  <vector>
//容器头文件,相比数组的优点是在于能根据需要随时自动调整自身大小以便容下所有元素

using namespace std;

class SHAPEDLL_EXPORT ShapeDll
{
public:
    ShapeDll();
};
class CPoint;
class CRect;
class SHAPEDLL_EXPORT CShape
{
public:
    CShape();
    //CShape(string strName):m_sName(){};
    CShape(const CShape & shape);
    virtual ~CShape();                       //设置为虚函数实现析构函数的多态性
    virtual double GetArea() const;
    virtual bool ptIn(const CPoint& pt) const;
    virtual bool InRect(const CRect& rc) const;
    virtual void Draw(QPainter & painter) const;
    virtual CShape* Clone() const;
    virtual CShape& Move(int nOffsetX,int nOffsetY);
protected:
    string  m_sName;
};
class SHAPEDLL_EXPORT CPoint:public CShape
{
    public:
    int m_nPosX;
    int m_nPosY;
    CPoint();                              //如果基类没有默认构造函数,则派生类也会无此方法
    CPoint(int nPosX,int nPosY);
    CPoint(const CPoint & pt);
    virtual ~CPoint();                     //设置为虚函数实现析构函数的多态性
    double GetArea() const;
    bool ptIn(const CPoint& pt) const;
    bool InRect(const CRect& rc) const;
    void Draw(QPainter & painter) const;
    CPoint* Clone() const;                      //呈现多态的方式一:指针
    CPoint& Move(int nOffsetX,int nOffsetY);    //呈现多态的方式二:引用
};
//绘制矩形类
class SHAPEDLL_EXPORT CRect:public CShape
{
public:
    CPoint m_ptLT;
    CPoint m_ptBR;

    CRect(CPoint pt1,CPoint pt2);
    CRect(const CRect & rc);
    virtual ~CRect();
    double GetArea() const;
    bool ptIn(const CPoint& pt) const;
    bool InRect(const CRect& rc) const;
    void Draw(QPainter & painter) const;
    CRect* Clone() const;
    CRect& Move(int nOffsetX,int nOffsetY);

};
class SHAPEDLL_EXPORT CRectPoint:public CRect,public CPoint
{
public:
    CRectPoint(CPoint pt1,CPoint pt2);
    CRectPoint(const CRectPoint &rc);
    virtual ~CRectPoint();
    double GetArea() const;
    bool ptIn(const CPoint& pt) const;
    bool InRect(const CRect& rc) const;
    void Draw(QPainter & painter) const;
    CRectPoint* Clone() const;
    CRectPoint& Move(int nOffsetX,int nOffsetY);


};
//图形管理类
class ShapesManager
{
public:
private:
     vector<CShape*> m_pShapes;
     ShapesManager();
     ~ShapesManager();
     void AddShape(CShape* pShape);            //添加一个图形
     void DelShape(CShape* pShape);            //删除一个图形
     void AddShapes(vector<CShape*> pShape);
     void DelShapes(vector<CShape*> pShape);
     void DelAll();                            //删除所有图形

};

#endif // SHAPEDLL_H

2.4.2 Shapedll.cpp

#include "shapedll.h"
#include<iostream>
using namespace std;
ShapeDll::ShapeDll()
{
}
CShape::CShape()
{

}
CShape::CShape(const CShape & shape)
{
     m_sName = shape.m_sName;
}
CShape::~CShape()
{
     cout<<"CShape::~CShape()\n";
}
double CShape::GetArea() const
{
     return 0;
}
bool CShape::ptIn(const CPoint& pt) const
{
    return false;
}
bool CShape::InRect(const CRect& rc) const
{
    return false;
}
void CShape::Draw(QPainter & painter) const
{

}
CShape* CShape::Clone() const
{
     return new CShape(*this);
}
CShape& CShape::Move(int nOffsetX,int nOffsetY)
{
    return *this;
}
//Cpoint成员函数实现
CPoint::CPoint()
{

}
CPoint::~CPoint()
{
    cout<<"CPoint::~CPoint()\n";
}
CPoint::CPoint(int nPosX,int nPosY)
{
    m_nPosX = nPosX;
    m_nPosY = nPosY;
}
CPoint::CPoint(const CPoint & pt)
{
    m_nPosX = pt.m_nPosX;
    m_nPosY = pt.m_nPosY;
}

double CPoint::GetArea() const
{
    return 0;
}
//判断某个点是否在图形范围内
bool CPoint::ptIn(const CPoint& pt) const
{
    return (pt.m_nPosX-m_nPosX)*(pt.m_nPosX-m_nPosX)+
            (pt.m_nPosY-m_nPosY)*(pt.m_nPosY-m_nPosY)<4;
}
//判断图形是否被选中
bool CPoint::InRect(const CRect& rc) const
{
    return (m_nPosX>rc.m_ptLT.m_nPosX && m_nPosX<rc.m_ptBR.m_nPosX &&
            m_nPosY>rc.m_ptLT.m_nPosY && m_nPosY<rc.m_ptBR.m_nPosY);
}
void CPoint::Draw(QPainter & painter) const
{
    painter.drawPoint(m_nPosX,m_nPosY);          //绘制点,通过指定端点坐标绘制,只能绘制单个点(点的X坐标,Y坐标)
    //若要绘制多个点则使用drawPoints()方法
}
CPoint* CPoint::Clone() const                    //拷贝构造函数
{
    return new CPoint(m_nPosX,m_nPosY);
    //等同于//return new CPoint(*this);
}
CPoint& CPoint::Move(int nOffsetX,int nOffsetY)
{
    m_nPosX +=nOffsetX;
    m_nPosX +=nOffsetY;
    return *this;
}

//CRect成员函数实现
CRect::CRect(CPoint pt1,CPoint pt2)  //矩形的左上和右下角
{
    m_ptLT =pt1;
    m_ptBR =pt2;
}
CRect::CRect(const CRect & rc)
{
    m_ptLT = rc.m_ptLT;
    m_ptBR = rc.m_ptBR;
}
CRect::~CRect()
{
    cout<<"CRect::~CRect()\n";
}
double CRect::GetArea() const
{
     //return (m_ptBR.m_nPosX‐m_ptLT.m_nPosX)*(m_ptBR.m_nPosY‐m_ptLT.m_nPosY);
}
bool CRect::ptIn(const CPoint& pt) const   //判断点是否在矩形内
{   //第一个const表示在此函数内不能修改pt,第二个const表示在该方法下不能修改CRect类中任何成员
    return (pt.m_nPosX>m_ptLT.m_nPosX &&pt.m_nPosX<m_ptBR.m_nPosX &&
            pt.m_nPosY>m_ptLT.m_nPosY &&pt.m_nPosY<m_ptBR.m_nPosY);
}
bool CRect::InRect(const CRect& rc) const
{
    return rc.ptIn(m_ptLT) && rc.ptIn(m_ptBR);
}
void CRect::Draw(QPainter & painter) const
{
   //绘制矩形
    painter.drawRect(m_ptLT.m_nPosX,m_ptBR.m_nPosY,      //左上点,右下点
                     m_ptBR.m_nPosX - m_ptLT.m_nPosX,    //矩形的宽
                     m_ptBR.m_nPosY-m_ptLT.m_nPosY);     //矩形的高
}
CRect* CRect::Clone() const
{
    return new CRect(*this);
}
//void CRect::Move(int nOffsetX,int nOffsetY);  //没有返回值则则只能move一次
CRect& CRect::Move(int nOffsetX,int nOffsetY)   //有返回值可连续move
{
    m_ptLT.m_nPosX += nOffsetX;
    m_ptLT.m_nPosY += nOffsetY;

    m_ptBR.m_nPosX += nOffsetX;
    m_ptBR.m_nPosY += nOffsetY;

    return *this;
}

//CRectPoint成员函数实现
CRectPoint::CRectPoint(CPoint pt1,CPoint pt2):
    CRect(pt1,pt2),CPoint((pt1.m_nPosX+pt1.m_nPosX)/2,(pt1.m_nPosY+pt1.m_nPosY)/2)
{

}
CRectPoint::CRectPoint(const CRectPoint &rc):
    CRect(rc.m_ptLT,rc.m_ptBR),CPoint(rc.m_nPosX,rc.m_nPosY)
{

}
CRectPoint::~CRectPoint()
{

}
double CRectPoint::GetArea() const
{
    return 0;
}
bool CRectPoint::ptIn(const CPoint& pt) const     //判断点是否在图形上
{
    return CRect::ptIn(pt);
}
bool CRectPoint::InRect(const CRect& rc) const
{
     return CRect::InRect(rc);
}
void CRectPoint::Draw(QPainter & painter) const
{
    CRect::Draw(painter);
    CPoint::Draw(painter);

}
CRectPoint* CRectPoint::Clone() const
{
     return new CRectPoint(*this);
}

CRectPoint& CRectPoint::Move(int nOffsetX,int nOffsetY)
{
    CRect::Move(nOffsetX,nOffsetY);
    CPoint::Move(nOffsetX,nOffsetY);
    return *this;
}
//ShapesManager的成员函数
void ShapesManager::AddShape(CShape * pShape)
{
    m_pShapes.push_back(pShape);
}
void ShapesManager::DelShape(CShape * pShape)
{
    for(vector<CShape*>::iterator it=m_pShapes.begin();
        it!=m_pShapes.end();it++)
    {
        if((*it) != pShape)
        {
            delete pShape;
            m_pShapes.erase(it);
            break;
        }
    }
}
void ShapesManager::AddShapes(vector<CShape*> pShape)
{
    m_pShapes.insert(m_pShapes.begin(),pShape.begin(),pShape.end());
}
void ShapesManager::DelShapes(vector<CShape*> pShape)
{
    for (vector<CShape*>::iterator it = m_pShapes.begin();it!=m_pShapes.end();it++)
    {
        DelShape(*it);
    }
}
void ShapesManager::DelAll()
{
    for (vector<CShape*>::iterator it = m_pShapes.begin();it!=m_pShapes.end();it++)
    {
        delete *it;
    }
    m_pShapes.clear();
}


void Test()
{
    CShape *pShape = new CPoint();
    delete pShape;
    pShape = new CRect(CPoint(0,0),CPoint(100,100));
    pShape->Move(10,10).Move(20,20).Move(40,40);
}


总结

1.继承权限

用protected,private继承的不能被外部访问,可以用using提升访问权限到public,改变后该成员在派生类的访问权限由using声明语句之前的派生类访问说明符确定。

2.多态性:是指具有不同功能的函数可以用一个函数名,这样就可以用一个函数名调用不同内容的函数。在C++中分为静态多态和动态多态两种,在程序运行前就完成联编的称为静态多态,主要通过函数重载和模板实现,动态多态在程序运行时才完成联编,主要通过虚函数实现。

函数重载:

void print_hello(string name)

{
cout << "hello " << name << endl;;
}

void print_hello(string name1, string name2)

{
cout << "hello, " << name1<< " " << name2 << endl;
}

虚函数:

class A
{
public:
    virtual void fun()
    {
        cout << “hello A” << endl;
    }
};
class B:public A
{
    public:
    virtual void fun()
    {
        cout << “hello B” << endl;
    }
}
int main()
{
    A a = new A();
    a->fun();
    a = new B();
    a->fun();
    return 0;
}
out:
hello A
hello B

对于一个继承体系来说,如果在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。

虚函数:用virtual声明的函数

虚函数的声明:  virtual 函数类型 函数名(形参表);
虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候。
在派生类中可以对基类中的成员函数进行覆盖。
虚函数一般不声明为内联函数,因为对虚函数的调用需要动态绑定,而对内联函数的处理是静态的。

派生类可以不显式地用virtual声明虚函数,这时系统就会用以下规则来判断派生类的一个函数成员是不是虚函数:
(1)该函数是否与基类的虚函数有相同的名称、参数个数及对应参数类型;
(2)该函数是否与基类的虚函数有相同的返回值或者满足类型兼容规则的指针、引用型的返回值;
如果从名称、参数及返回值三个方面检查之后,派生类的函数满足上述条件,就会自动确定为虚函数。这时,派生类的虚函数便覆盖了基类的虚函数。一般也会将派生类中的函数用virtual修饰,方便识别其为虚函数。

通过这次实验我学习了C++中类的三种继承方式权限的区别,以及用using语句提升部分权限,还测试使用了友元函数,多态性。但是没有掌握矢量图的设置,以及如何可视化来进行测试绘制矢量图的相关函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值