IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++复习日记1 类的继承与派生 -> 正文阅读

[C++知识库]c++复习日记1 类的继承与派生

8月16日,回顾一下c++继承与派生的知识。

继承是面向对象程序设计中软件重用的关键技术。类与类间的关系主要分has-a、uses-a、is-a三种,其中is-a有传递性,此种机制即为继承。

派生类对基类成员的访问权限即类的继承中的“访问控制”可以总结成”公不变,私全私,保全保”,当然都不包括基类的私有成员。但是,派生类并非不创建从基类继承的私有数据成员(派生类可以通过所继承的该基类的成员函数,来访问基类私有数据成员)。除此之外,c++中还提供了一种访问调节机制,可以通过声明基类名::成员,保持私有继承时的基类公有数据成员在派生类中仍为公有数据成员。但声明数据成员时不可以带类型,也不可以在派生类中降低或者升高数据成员的可访问性

c++允许派生类的成员函数与基类的同名,并且派生类中访问时会自动屏蔽基类的同名数据成员。如果是成员函数同名,则在派生类中相当于重载这个函数。由调用形式指示this指针的不同类型,来调用不同版本的成员函数。

c++中,可以在派生类中初始化基类数据成员,用到了构造函数的执行顺序,这里不再赘述。

c++中继承还有多继承和虚继承等方式。多继承顾名思义,一个派生类可以继承多个基类。虚继承是为了避免继承了同一基类A的两个派生类B和C,又作为基类被另一个类D多继承时,A的成员在D中被划分为从B继承和从C继承所产生的二义性。虚继承可以通过把DAG图的会点定义为虚基类,即在B和C继承A时,用class B::virtual public A的格式实现。

下面来看一个经典的类的继承例子:

//Point.h
#ifndef POINT_H
#define POINT_H
class Point
{
    friend ostream & operator<<(ostream &, const Point &); ?//friend友元函数对操作符<<进行重载
public:
    Point(int = 0, int = 0); ?//带默认参数的构造函数
    void setPoint(int, int); ?//对点坐标数据赋值
    int getX()const
    {
        return x;
    }
    int getY() const
    {
        return y;
    };
protected:
    int x, y;
};
Point::Point(int a, int b)
{
    setPoint(a, b);
}
void Point::setPoint(int a, int b)
{
    x = a;y = b;
}
//重载插入运算符,输出对象数据
ostream & operator<<(ostream& output, const Point& p)
{
    output << '[' << p.x << "," << p.y << "]";
    return output;
}
#endif

//Circle.h
#ifndef CIRCLE_H
#define CIRCLE_H
class Circle :public Point
{
    friend ostream& operator <<(ostream&, const Circle&);
public:
    Circle(double r = 0, int x = 0, int y = 0); ? //构造函数
    void setRadius(double); ? //置半径值
    double getRadius() const; ? ?//返回半径
    double area() const; ? //返回面积
protected:
    double radius; ? ?//数据成员,半径
};
Circle::Circle(double r, int a, int b) :Point(a, b) { setRadius(r); }; ?//带参数构造函数初始化,首先调用基类构造函数
void Circle::setRadius(double r) { radius = r; };
double Circle::getRadius() const { return radius; };
double Circle::area() const { return 3.14 * radius * radius; };
ostream& operator <<(ostream& output, const Circle& c)
{
    output << "Center =" << '[' << c.x << ',' << c.y << "]" << ":Radius =" << setiosflags(ios::fixed | ios::showpoint) << setprecision(2) << c.radius;
    return output;
}
#endif // !CIRCLE_H
?

//Cylinder.h
#ifndef CYLINDER_H
#define CYLINDER_H
class Cylinder :public Circle
{
    friend ostream& operator<<(ostream&, const Cylinder&);
public:
    Cylinder(double h = 0.0, double r = 0.0, int x = 0, int y = 0);
    void setHeight(double);
    double getHeight() const;
    double area() const;
    double volume() const;
protected:
    double height;
};
Cylinder::Cylinder(double h, double r, int x, int y) :Circle(r, x, y) { setHeight(h); };
void Cylinder::setHeight(double h) { height = h; };
double Cylinder::getHeight()const { return height; };
double Cylinder::area() const { return 2 * Circle::area() + 3.14 * radius * height; };
double Cylinder::volume() const { return Circle::area() * height; };
ostream& operator<<(ostream& output, const Cylinder& cy)
{
    output << "Center =" << '[' << cy.x << "," << cy.y << "]" << ":Radius =" << setiosflags(ios::fixed | ios::showpoint) << setprecision(2) << cy.radius << ":Height =" << cy.height << endl;
    return output;
}
#endif // !CYLINDER_H
 

main.cpp包含了以上三个头文件,实现对给定圆心平面坐标、半径与高,计算圆柱体体积等。

//main.cpp
#include <iostream>
#include<iomanip>
using namespace std;
#include "Point.h"
#include "Circle.h"
#include"Cylinder.h"
int main()
{
    Point p(72, 115);
    cout << "The initial location of p is" << p << endl;
    p.setPoint(10, 0);
    cout << "\n The new location of p is" << p << endl;
    Circle c(2.5, 37, 43);
    cout << "\n The initial location and radius of circle is" << c << "\n Area is" << c.area() << "\n";
    c.setRadius(4.25);
    c.setPoint(2, 2);
    cout << "\n The new location and radius of circle is" << c << "\n Area is" << c.area() << "\n";
    Cylinder cy1(5.7, 2.5, 12, 23);
    cout << "\n The initial location ,radius and height of cylinder is" << cy1 << "\n Area is" << cy1.area() << "\n"<<"\n volume is"<<cy1.volume()<<"\n";
    return 0;
}

输出结果:

The initial location of p is[72,115]
?
 The new location of p is[10,0]
?
 The initial location and radius of circle isCenter =[37,43]:Radius =2.50
 Area is19.62
?
 The new location and radius of circle isCenter =[2,2]:Radius =4.25
 Area is56.72
?
 The initial location ,radius and height of cylinder isCenter =[12,23]:Radius =2.50:Height =5.70
?
 Area is84.00
?
 volume is111.86

在本例中,Point类定义了私有数据成员坐标x,y,并提供通过公有成员函数getX()和getY()作为访问接口。对x,y赋值可以通过创建对象时的初始化对象调用带参数的构造函数,或者用setPoint()函数。重载了运算符"<<",输出对象时直接输出对象的坐标。前几天也复习了运算符重载,将在后面记录和讨论。

Circle类由Point类派生,定义了受保护的数据成员半径radius,并提供了公有成员函数接口可以访问。还定义了面积计算函数area()。Circle类构造函数有三个参数,通过调用基类Point的带参构造函数初始化x,y坐标。

Cylinder类由Circle类派生,定义了受保护的数据成员height,并拥有从Circle和Point继承的x,y和radius。构造函数含默认参数,通过调用Circle类的构造函数直接初始化x,y和radius三个数据成员。类内函数完成对圆柱表面积和体积的计算(计算面积时,area()与Circle类中的为同名函数,但开辟的是不同的储存空间,在派生类中也会屏蔽基类同名函数)。最后再次重载"<<"运算符,覆盖掉基类重载版本,输出一系列圆柱属性。


上面用到了对运算符”<<“的重载,下面来回顾一下c++中运算符重载。

顾名思义,重载运算符就是让标准库函数的运算符按照用户的意愿去重新加载,但原有的语义一般不变。重载运算符格式为:

类型 类名::operator op(参数表)
{
 ? ?//用户自定义的操作
}

用于类的运算符通常都要重载,但c++也提供两个算符的默认重载版本:

"="默认重载为对象数据成员的复制

"&"默认重载为返回任何类对象的地址

通常重载运算符用于成员函数和友元函数,因为普通函数重载访问非公有类成员时需通过public接口提供的函数实现,增加程序开销。用友元函数重载运算符时,左右操作数都由参数传递,可以有效解决运算符左右操作数类型不同导致的问题。来看看下面的复数运算例子:

/*友元函数重载运算符---复数运算*/
/*如果用友元函数重载运算符,左右操作数都由参数传递,则c++可以通过构造函数实现数据的隐式转换*/
#include <iostream>
using namespace std;
class Complex
{
public:
 ? ?Complex(double r = 0, double i = 0);
 ? ?Complex(int a)
 ?  {
 ? ? ? ?Real = a;
 ? ? ? ?Image = 0;
 ?  }
 ? ?void print() const;
 ? ?friend Complex operator+(const Complex& c1, const Complex& c2); //如果以友元函数重载,则可以使用应用参数作为修改对象
 ? ?friend Complex operator-(const Complex& c1, const Complex& c2);
 ? ?friend Complex operator-(const Complex& c1);
private:
 ? ?double Real ,Image;
};
Complex::Complex(double r, double i)
{
 ? ?Real = r;Image = i;
}
Complex operator+(const Complex& c1, const Complex& c2)
{
 ? ?double r = c1.Real + c2.Real;
 ? ?double i = c1.Image + c2.Image;
 ? ?return Complex(r, i);
}
Complex operator-(const Complex& c1, const Complex& c2)
{
 ? ?double r = c1.Real - c2.Real;
 ? ?double i = c1.Image - c2.Image;
 ? ?return Complex(r, i);
}
Complex operator-(const Complex& c)
{
 ? ?return Complex(-c.Real ,-c.Image);
}
void Complex::print() const
{
 ? ?cout << "REAL=" << Real << "IMAGE=" << Image<<endl;
}
int main()
{
 ? ?Complex c1(1, 2), c2(3, 3);
 ? ?Complex c;
 ? ?c = c1 - c2;
 ? ?c.print();
 ? ?c = c1 + c2;
 ? ?c.print();
 ? ?c = c2 + 2;
 ? ?c.print();
 ? ?c = 2 + c2;
 ? ?c.print();
 ? ?c = -c1;
 ? ?c.print();
}
?

运行结果:

REAL=-2IMAGE=-1
REAL=4IMAGE=5
REAL=5IMAGE=3
REAL=5IMAGE=3
REAL=-1IMAGE=-2

如果将

friend Complex operator+(const Complex& c1, const Complex& c2);

这一句换成

Complex operator+(Complex);

则下面的”c2+2“被解释为c2.operator+(25),但"2+c2"则被解释为25.operator+(c2),25不是Complex类对象,无法驱动函数进行计算。定义为友元函数就可以解决这个问题。

再来看一个经典的运算符重载例子,也是上面继承例程中出现过的"<<"的重载:

/*经典重载运算符"<<"与">>"---Vector类*/
#include <iostream>
using namespace std;
class Vector
{
public:
    Vector(int = 1); ? ? //默认长度构造函数
    Vector(const int*, int); ? //使用数组参数构造函数
    Vector(const Vector&); ? //拷贝构造函数
    ~Vector(); ? ?//析构函数
    //重载运算符
    int& operator[](int i)const;
    int operator()()const;
    Vector& operator=(const Vector&);
    bool operator==(const Vector&)const;
    bool operator!=(const Vector&)const;
    friend Vector operator+(const Vector&, const Vector&);
    friend ostream& operator<<(ostream& output, const Vector&);
    friend istream& operator>>(istream& input, Vector&);
private:
    int* v;
    int len;
};
//构造指定长度向量,初始化数据元素为0
Vector::Vector(int size)
{
    if (size < 0 || size>100)
    {
        cout << "error";
        exit(0);
    }
    v = new int[size];
    for (int i = 0; i < size; i++)
    {
        v[i] = 0;
        len = size;
    }
}
//用整型数组构造向量
Vector::Vector(const int* B,int size)
{
    if (size < 0 || size>100)
    {
        cout << "error";
        exit(0);
    }
    v = new int[size];
    len = size;
    for (int i = 0;i < size;i++)
    {
        v[i] = B[i];
    }
}
//用已有对象复制构造向量
Vector::Vector(const Vector& c)
{
    len = c();
    v = new int[len];
    for (int i = 0;i < len;i++)
    {
        v[i] = c[i];
    }
}
//析构函数
Vector::~Vector()
{
    delete[] v;
    len = 0;
}
//返回向量元素
int &Vector::operator[](int i) const
{
    if (i >= 0 && i <= 100) return v[i];
    else cout << "out of range" << endl;
    exit(0);
}
//返回向量长度
int Vector::operator()()const
{
    return len;
}
//向量赋值
Vector& Vector::operator=(const Vector &B)
{
    if (len == B())
    {
        for (int i = 0;i < len;i++)
        {
            v[i] = B.v[i];
            return *this;
        }
    }
    else
    {
        cout << "operate= fail\n";
        exit(0);
    }
}
//判断两个向量相等
bool Vector::operator==(const Vector& B) const
{
    if (len == B.len)
    {
        for (int i = 0;i < len;i++)
        {
            if(v[i] != B.v[i])
             ?return false;
        }
    }
    else
        return false;
    return true;
}
//判断两向量不等
bool Vector::operator!=(const Vector& B) const
{
    return!(*this == B); ? //调用(*this).operator==(B)
}
//向量相加
Vector operator+(const Vector& A, const Vector& B)
{
    int size = A();
    int* T = new int[size];
    if (size == B())
    {
        for (int i = 0; i < size; i++)
            T[i] = A.v[i] + B.v[i];
        return Vector(T, size); ? //用数组构造返回对象
    }
    else
    {
        cout << "operator+ fail\n";
        exit(0);
    }
}
//输出向量
ostream& operator<<(ostream& output, const Vector& A)
{
    for (int i = 0;i < A.len;i++)
        output << A.v[i] << " ";
    return output;
}
//输入向量
istream& operator>>(istream& input, Vector& A)
{
    for (int i = 0;i < A();i++)
        input >> A.v[i];
    return input;
}
int main()
{
    int k;
    cout << "Input the length and Vector:\n";
    cin >> k;
    Vector A(k), B(k), C(k); ? ? //构造指定长度向量
    cout << "Input the element of Vector A:\n";
    cin >> A; ? ? ? ? //调用operatoe>>(cin,A)
    cout << "Input the element of Vector B:\n";
    cin >> B; ? ? ? ? ?//调用operatoe<<(cin,B)
    if (A == B) ? ? ? //调用A.operator==(B)
    {
        for (int i = 0;i < A();i++)
            C[i] = A[i] * 2; ? ?//调用C.operator[](i)和A.operator[](i)
    }
    else C = A + B; ? ? ?//调用operator+(A,B)和C.operator=(A+B)
    cout << "[" << A << "]\n+[" << B << "]\n=[" << C << "]" << endl;
}

对于这个例子,关键分析重载插入和提取运算符。

istream& operator>>(istream& input, Vector& A)
{
    for (int i = 0;i < A();i++)
        input >> A.v[i];
    return input;
}

主函数中调用cin>>A,解释为operator>>(cin,A),第一个引用是返回对istream的引用,用于连续进行插入操作。第二个引用是istream类对象的引用,用>>把数据传到input里。第三个引用将Vector类对象A地址直接传入,如果省去&就会复制A的参数传入,增加了程序开销。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:11:24  更:2021-08-17 15:15:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 6:19:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计