سلام!

در این مبحث می خواهیم یک قابلیت بسیار کاربردی را برای کلاس ها (و وراثت) آموزش دهیم. فرض کنید کدی مانند کد زیر را نوشته اید. این کد چیز خاصّی ندارد. یک کلاس Shape هست با دو وارث با نام های Square و Circle. کلاس Shape یک string دارد به نام type که در آن نوع شکل ذخیره می شود.

#include <iostream>
#include <string>
#include <cmath>

using namespace std;

class Shape{
    protected:
        string _type;
    public:
        
        Shape( string type = "Shape" ) : _type( type ) {}

        string type();

        void setType( string = "" );

        string area();

};

string Shape::type(){
    return this->_type;
}

void Shape::setType( string type ){
    this->_type = type;
}

string Shape::area(){
    return "not defined";
}

class Square : public Shape{
    protected:
        double _width;
    public:

        Square( double width = 0 ) : _width( width ) , Shape( "Square" ) {}

        double width();
        
        void setWidth( double = 0 );

        double area();
};

double Square::width(){
    return this->_width;
}

void Square::setWidth( double width ){
    this->_width = width;
}

double Square::area(){
    return pow( this->width(), 2 );
}

class Circle : public Shape{
    protected:
        double _radius;
    public:
        
        Circle( double radius = 0 ) : _radius( radius ), Shape( "Circle" ) {}

        double radius();

        void setRadius( double = 0 );

        double area();
};

double Circle::radius(){
    return this->_radius;
}

void Circle::setRadius( double radius ){
    this->_radius = radius;
}

double Circle::area(){
    return pow( this->radius(), 2) * acos( -1 );
}

int main(){
    Circle circle( 10 );
    Square square( 10 );
    Shape shape;
    cout << "I am a " << circle.type() << " and my area is " << circle.area() << endl;
    cout << "I am a " << square.type() << " and my area is " << square.area() << endl;
    cout << "I am a " << shape.type() << " and my area is " << shape.area() << endl;
    return 0;
}

حالا یک اشاره گر با نوع کلاس Shape می سازیم و آدرس آن را برابر با آدرس یک Object از Square قرار می دهیم، یعنی

Shape *shape = &square;

قبل از این که اتفاقاتی را که در این جا رخ می دهد توضیح دهم لازم است توجّه شما را به نکته ای جلب کنم که قبلا هم گفته شده است. کلاس پایه به هیچ کدام از عضو های کلاس وارث دسترسی ندارد! خب، حالا اتفاقی که این جا می افتد را توضیح می دهم. اگر این خط کد را در برنامه ای که اوّل جلسه به آن اشاره کردم قرار دهید، متوجّه می شوید که اگر در برنامه ی خود بنویسید

cout << shape->type();

خروجی برنامه Square خواهد بود.

متغیر type در شی square برابر با Square بوده است. این متغیر در کلاس Shape تعریف شده و بنابراین کلاس Shape به آن دسترسی دارد. وقتی که آدرس اشاره گر به کلاس پایه را برابر با آدرس کلاس وارث قرار می دهیم، کامپایلر تمامی متغیّرهای موجود در کلاس پایه را در اشاره گر به کلاس پایه می ریزد و کار دیگری هم انجام نمی دهد.

همین مطلب راجع به متغیرهای مرجع هم صدق می کند.

حالا شاید از خودتان بپرسید که این قابلیّت چه فایده ای دارد؟ مثلا در همین مثال بالا؛ تابع های زیر را در نظر بگیرید؛

void introduction( const Square &square ){
    cout << "I am a " << square.type();
}

void introduction( const Circle &circle ){
    cout << "I am a " << circle.type();
}

نوشتن این توابع بدون استفاده از قابلیّت بالا برای هر کدام از کلاس های وارث لازم است. اما می توانید همه ی توابع ممکن را در این تابع خلاصه کنید.

void introduction( Shape *shape ){
    cout << "I am a " << shape->type();
}

فعلا در این رابطه مطلب دیگری نیاز نیست.

موفق باشید!