Тема: Доступ до protected змінних базового класу

Вітаю Вас форумчани!
Я займаюсь по книзі Герберта Шилдта і виконую вправи по динамічній ідентифікації типів.
Маю дві подібні програмки
№1 компілюється нормально:

/* Dynamic types identification demonstration */
#include <iostream>
#include <cstdlib>
#include <typeinfo>
using namespace std;

class figure {
protected:
    double x, y;
public:
    figure(double i, double j) {
        x = i;
        y = j;
    }
    virtual double area() = 0;
};

class triangle : public figure {
public:
    triangle(double i, double j) : figure(i, j) {}
    double area() {
        return x * 0.5 * y;
    }
};

class rectangle : public figure {
public:
    rectangle(double i, double j) : figure(i, j) {}
    double area() {
        return x * y;
    }
};

class circle : public figure {
public:
    circle(double i, double j=0) : figure(i, j) {}
    double area() {
        return 3.14 * x * x;
    }
};

/* Fabric of objects of class figure */
figure *factory() {
    switch(rand() % 3) {
        case 0: return new circle(10.0);
        case 1: return new triangle(10.1, 5.3);
        case 2: return new rectangle(4.3, 5.7);
    }
    return 0;
}

int main() {
    figure *p; /* pointer to the base class */
    int i;
    
    int t = 0, r = 0, c = 0;
    
    /* generating and counting objects */
    for (i = 0; i < 10; i++) {
        p = factory(); /* generating object */
        cout << "Object has type " << typeid(*p).name();
        cout << ". ";
        
        /* keeping this object in mind */
        if(typeid(*p) == typeid(triangle)) t++;
        if(typeid(*p) == typeid(rectangle)) r++;
        if(typeid(*p) == typeid(circle)) c++;
    
        /* Output square of the figure */
        cout << "The square of the figure is" << p->area() << endl;
    }
    
    cout << endl;
    cout << "There was generated next objects:\n";
    cout << "  triangles: " << t << endl;
    cout << "  rectangles: " << t << endl;
    cout << "  circles: " << c << endl;
    
    return 0;
}

№2 Ця ж програма, але із динамічним визначенням типу:

/* Template version of figure hierarchy */
#include <iostream>
#include <cstdlib>
#include <typeinfo>
using namespace std;

template <class T> class figure {
protected:
    T x, y;
public:
    figure(T i, T j) {
        x = i;
        y = j;
    }
    virtual T area() = 0;
};

template <class T>  class triangle : public figure<T> {
public:
    triangle(T i, T j) : figure<T>(i, j) {}
    T area() {
        return x * 0.5 * y;
    }
};

template <class T> class rectangle : public figure<T> {
public:
    rectangle(T i, T j) : figure<T>(i, j) {}
    T area() {
        return x * y;
    }
};

template <class T> class circle : public figure<T> {
public:
    circle(T i, T j=0) : figure<T>(i, j) {}
    T area() {
        return 3.14 * x * x;
    }
};

/* Fabric of objects of class figure */
figure<double> *generator() {
    switch(rand() % 3) {
        case 0: return new circle<double>(10.0);
        case 1: return new triangle<double>(10.1, 5.3);
        case 2: return new rectangle<double>(4.3, 5.7);
    }
    return 0;
}

int main() {
    figure<double> *p; /* pointer to the base class */
    int i;    
    int t = 0, r = 0, c = 0;
    
    /* generating and counting objects */
    for (i = 0; i < 10; i++) {
        p = generator(); /* generating object */
        cout << "Object has type " << typeid(*p).name();
        cout << ". ";
        
        /* keeping this object in mind */
        if(typeid(*p) == typeid(triangle<double>)) t++;
        if(typeid(*p) == typeid(rectangle<double>)) r++;
        if(typeid(*p) == typeid(circle<double>)) c++;
    
        /* Output square of the figure */
        cout << "The square of the figure is" << p->area() << endl;
    }
    
    cout << endl;
    cout << "There was generated next objects:\n";
    cout << "  triangles: " << t << endl;
    cout << "  rectangles: " << t << endl;
    cout << "  circles: " << c << endl;
    
    return 0;
}

Компілятор не компілює другу програму і видає помилки:

main.cpp:22:16: error: ‘x’ was not declared in this scope
         return x * 0.5 * y;
                ^
main.cpp:22:26: error: ‘y’ was not declared in this scope
         return x * 0.5 * y;

Чому в першій програмі ми можемо отримати доступ до x і y, а в другій ні?

Білий Лунь

2 Востаннє редагувалося koala (13.11.2015 13:55:03)

Re: Доступ до protected змінних базового класу

Тут якраз складний випадок, треба тягнути батьківські елементи за допомогою using або використовувати this:

template <class T>  class triangle : public figure<T> {
protected:
    using figure<T>::x;
    using figure<T>::area;//ага, ваша area просто перекривала батьківську, а не перевантажувала
public:
    triangle(T i, T j) : figure<T>(i, j) {}
    T area() {
        return x * 0.5 * this->y;
    }
};

Зате з

template <class T>  class triangle : public figure<double> {

все має працювати нормально.

Детальний розбір (англ): http://stackoverflow.com/questions/4643 … is-pointer

Подякували: Arete, Yola, Ярослав, coder, leofun015

3 Востаннє редагувалося -=ЮрА=- (13.11.2015 22:37:49)

Re: Доступ до protected змінних базового класу

Ярослав, я би відкрив видимість за допомогою figure<T>::, також зазначу, що у студії код збирається відразу без якихось доопрацювань, тому більше схилен вважати що такі вибрики більше походять від реалізації компілера

/* Template version of figure hierarchy */
#include <iostream>
#include <cstdlib>
#include <typeinfo>
using namespace std;
 
template <class T> class figure {
protected:
    T x, y;
public:
    figure(T i, T j) {
        x = i;
        y = j;
    }
    virtual T area() = 0;
};
 
template <class T>  class triangle : public figure<T> {
public:
    triangle(T i, T j) : figure<T>(i, j) {}
    T area() {
        return 0.5*figure<T>::x*figure<T>::y;
    }
};
 
template <class T> class rectangle : public figure<T> {
public:
    rectangle(T i, T j) : figure<T>(i, j) {}
    T area() {
        return figure<T>::x * figure<T>::y;
    }
};
 
template <class T> class circle : public figure<T> {
public:
    circle(T i, T j=0) : figure<T>(i, j) {}
    T area() {
        return 3.14 * figure<T>::x * figure<T>::x;
    }
};
 
/* Fabric of objects of class figure */
figure<double> *generator() {
    switch(rand() % 3) {
        case 0: return new circle<double>(10.0);
        case 1: return new triangle<double>(10.1, 5.3);
        case 2: return new rectangle<double>(4.3, 5.7);
    }
    return 0;
}
 
int main() {
    figure<double> *p; /* pointer to the base class */
    int i;    
    int t = 0, r = 0, c = 0;
    
    /* generating and counting objects */
    for (i = 0; i < 10; i++) {
        p = generator(); /* generating object */
        cout << "Object has type " << typeid(*p).name();
        cout << ". ";
        
        /* keeping this object in mind */
        if(dynamic_cast< triangle <double> *>(p)) t++;
        if(dynamic_cast< rectangle<double> *>(p)) r++;
        if(dynamic_cast< circle   <double> *>(p)) c++;
    
        /* Output square of the figure */
        cout << "The square of the figure is" << p->area() << endl;
    }
    
    cout << endl;
    cout << "There was generated next objects:\n";
    cout << "  triangles: " << t << endl;
    cout << "  rectangles: " << t << endl;
    cout << "  circles: " << c << endl;
    
    return 0;
}

http://codepad.org/YyjMJuIw

template <class T>  class triangle : public figure<double> {

- це явна спеціалізація без темплейту, рішення на мою думку хибне, більше схоже на костиль.

koala, я прийшов сюди не ругатися а обсудити, тому прошу прояснити незрозумілий для мене вислів

ага, ваша area просто перекривала батьківську, а не перевантажувала

- для віртуального методу це саме те що і повинно було бути, визивається імплеменція для конкретного наслідника.

Подякували: Ярослав, koala, Yola3

4

Re: Доступ до protected змінних базового класу

Мабуть, це означає, що я лоханувся.
Температура спаде - піду читати мани.

Подякували: coder1

5

Re: Доступ до protected змінних базового класу

koala написав:

Температура спаде - піду читати мани.

Йди негайно читай мани, температура не завадила запостити неправильну відповідь у тему.

I can tell, seen before, I know the way, I know the law
Can't believe, can't obey, Can't agree with all the things I hear you say

6 Востаннє редагувалося coder (14.11.2015 01:34:15)

Re: Доступ до protected змінних базового класу

/* Template version of figure hierarchy */
#include <iostream>
#include <cstdlib>
#include <typeinfo>
using namespace std;

template <class T> class figure {
protected:
    T x, y;
public:
    figure(T i, T j) {
        x = i;
        y = j;
    }
    virtual T area() = 0;
};

template <class T>  class triangle : public figure<T> {
public:
    triangle(T i, T j) : figure<T>(i, j) {}
    T area() {
        return figure<T>::x * 0.5 * figure<T>::y;
    }
};

template <class T> class rectangle : public figure<T> {
public:
    rectangle(T i, T j) : figure<T>(i, j) {}
    T area() {
        return figure<T>::x * figure<T>::y;
    }
};

template <class T> class circle : public figure<T> {
public:
    circle(T i, T j = 0) : figure<T>(i, j) {}
    T area() {
        return 3.14 * figure<T>::x * figure<T>::x;
    }
};

/* Fabric of objects of class figure */
figure<double> *generator() {
    switch (rand() % 3) {
    case 0: return new circle<double>(10.0);
    case 1: return new triangle<double>(10.1, 5.3);
    case 2: return new rectangle<double>(4.3, 5.7);
    }
    return 0;
}

int main() {
    figure<double> *p; /* pointer to the base class */
    int i;
    int t = 0, r = 0, c = 0;

    /* generating and counting objects */
    for (i = 0; i < 10; i++) {
        p = generator(); /* generating object */
        cout << "Object has type " << typeid(*p).name();
        cout << ". ";

        /* keeping this object in mind */
        if (typeid(*p) == typeid(triangle<double>)) t++;
        if (typeid(*p) == typeid(rectangle<double>)) r++;
        if (typeid(*p) == typeid(circle<double>)) c++;

        /* Output square of the figure */
        cout << "The square of the figure is" << p->area() << endl;
    }

    cout << endl;
    cout << "There was generated next objects:\n";
    cout << "  triangles: " << t << endl;
    cout << "  rectangles: " << t << endl;
    cout << "  circles: " << c << endl;

    return 0;
}

Правильно так якщо компілюєте на g++ або gcc. В gcc-3.4+ це вже виправлено. Попередні версії були більш суворі при наслідуванні від класів темплейтів.

А тепер пояснення:
The problem when compiling triangle is that its base class figure is unknown from the compiler, being a template class, so no way for the compiler to know any members from the base class.

http://content.screencast.com/users/Patronik/folders/Jing/media/20bdf56f-9d4d-4442-9237-bdd9eeacb1fc/2015-11-14_0028.png

I can tell, seen before, I know the way, I know the law
Can't believe, can't obey, Can't agree with all the things I hear you say
Подякували: -=ЮрА=-, Ярослав, Arete3

7

Re: Доступ до protected змінних базового класу

Всім дуже дякую, докладно і зрозуміло пояснили.
Як я зрозумів - компілятор створить реальні функції по шаблону тільки коли зустріне посилання на член класу, тому обов'язково треба вказати figure<T>::.

Білий Лунь