На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Проблема с полями объекта при наследовании
    Парочка вопросов. Использую Microsoft Visual Studio 2015.
    1)Как сделать так, чтобы в консоль во всех случаях выводилось значение переменной var?
    Если убрать определение поля "double x = 999" в классе SameText (оно не нужно, так как поле наследуется) то возникнет ошибка "Error C2385 ambiguous access of 'x'".
    ExpandedWrap disabled
      #include <stdio.h>
      #include <stdlib.h>
      #include <iostream>
      using namespace std;
       
      ///ABSTRCAT LIBRARY/////////////////////
      #define abstract
      abstract class Sprite{
      public:
          double x = 999;
          virtual void setX(double x)=0;
      };
      abstract class Text:public Sprite{
      public:
          virtual void setText(char* text)=0;
      };
      ///IMPLEMENTATION///////////////////////
      class SameSprite:public Sprite{
      public:
          void setX(double x){
              this->x = x;
          }
      };
      class SameText: public Text, public SameSprite{
      public:
          double x = 999;
          void setX(double x){
              this->x = x;
          }
          void setText(char* text){}
      };
      int main(){
          Sprite* sprite;
          Text* text;
          SameSprite* same;
          ///
          double var = 3;
          SameText* sameText = new SameText();
          sprite = (Sprite*)((SameSprite*)sameText);
          text = (Text*)sameText;
          same = (SameSprite*)sameText;
          same->x = var;
          cout<<sprite->x<<"\n";//3
          cout<<text->x<<"\n";//999
          cout<<same->x<<"\n";//3
          cout<<sameText->x<<"\n";//999
          system("PAUSE");
          return 0;
      }
    Можно 42-ю строчку заменить на "sameText->x = var;", по сути ничего не измениться, только в этот раз будет возвращено только одно значение 3, вместо двух.

    Вообще, даже если не рассматривать множественное наследование, я заметил странное поведение C++: если в классах-наслениках определять поля, которые присутствуют в базовых классах, тогда эти поля себя ведут просто как переменные с доступом через указатель на объект. Ведь базовый класс может быть абстрактным, экземпляр которого нельзя создавать, но его поле будет все равно доступно и иметь свои значения, как-будто экземпляр этого абстрактного класса создан. Вот пример, для наглядности. Здесь показано поведение при переопределении наследуемого поля и без этого (нормальное поведение). Хотя указатель типа базового абстрактного класса Sprite (sprite) и указывает на объект класса SameSprite (same), наследуемого от Sprite, это не мешает выражениям 'same->x' и 'sprite->x' возвращать те значения, которые им присвоены (разные), хотя объекта sprite в принципе не может существовать, значит и не должно выделятся память для хранения второго значения поля x, но мы видим обратное.
    ExpandedWrap disabled
      #include <stdio.h>
      #include <stdlib.h>
      #include <iostream>
       
      using namespace std;
      #define Message(s) MessageBox(0,s,"",0)
       
      #define abstract
      abstract class Sprite{
      public:
          double x = 999;
          virtual void setX(double x)=0;
      };
      class SameSprite:public Sprite{
      public:
          double x = 999;
          void setX(double x){
              this->x = x;
          }
      };
      class SameSprite2:public Sprite{
      public:
          void setX(double x){
              this->x = x;
          }
      };
      class SubSameSprite2:public SameSprite2{
      public:
          void setX(double x){
              this->x = x;
          }
      };
      int main(){
          Sprite* sprite;
          SameSprite* same = new SameSprite();
          SameSprite2* same2 = new SameSprite2();
          SubSameSprite2* subSame2 = new SubSameSprite2();
          sprite = same;
          sprite->x=1;
          cout<<same->x<<"\n";//999
          cout<<sprite->x<<"\n";//1
          cout<<"\\\\\\\n";
          sprite = same2;
          sprite->x=2;
          cout<<same2->x<<"\n";//2
          cout<<sprite->x<<"\n";//2
          cout<<"\\\\\\\n";
          sprite = subSame2;
          sprite->x = 4;
          cout<<same2->x<<"\n";//2
          cout<<subSame2->x<<"\n";//4
          cout<<sprite->x<<"\n";//4
          ///
          cout<<"Static objects test\n";
          SameSprite sameStatic;
          Sprite& spriteStatic = sameStatic;
          spriteStatic.x = 1;
          cout<<sameStatic.x<<"\n";//999
          cout<<spriteStatic.x<<"\n\\\\\\\n";//1
          system("PAUSE");
          return 0;
      }

    В полностью ООП языках, например в Java, попросту нельзя определять поля в наследниках, присутствующие в базовых классах, иначе возникает ошибка при компиляции. Поэтому попутно возникает еще один вопрос. 2) Это фича языка, или это особенность конкретной реализации, баг?
    Сообщение отредактировано: riden -
      Цитата riden @
      Если убрать определение поля "double x = 999" в классе SameText (оно не нужно, так как поле наследуется) то возникнет ошибка "Error C2385 ambiguous access of 'x'".
      Естественно. Их же там два. Один из this->Text::Sprite (левый предок), второй из this->SameSprite::Sprite (правый предок). Тебе какой надо менять?

      Добавлено
      Цитата riden @
      Вообще, даже если не рассматривать множественное наследование, я заметил странное поведение C++: ...
      Тут много чего сразу. Давай уж по пунктам, что ли.

      Добавлено
      Цитата riden @
      если в классах-наслениках определять поля, которые присутствуют в базовых классах, тогда эти поля себя ведут просто как переменные с доступом через указатель на объект
      Почему это? Или ты о this?
      В базовом классе своё поле, в производном своё. Ну, называются одинаково, ну и что? Тебя не смущает глобальная var, локальная в функции var и ещё for-параметр var? У них разные области видимости, ничего страшного.
      Цитата riden @
      Ведь базовый класс может быть абстрактным, экземпляр которого нельзя создавать, но его поле будет все равно доступно и иметь свои значения, как-будто экземпляр этого абстрактного класса создан.
      Можно создавать экземпляры абстрактных классов. Но – только в составе других объектов как их подобъекты. Секундочку, а как иначе-то? Если их экземпляры вообще нельзя создать никак, зачем они тогда вообще нужны-то? А значит поля абстрактных классов обязательно будут в окаймляющем объекте.

      Добавлено
      Цитата riden @
      ...хотя объекта sprite в принципе не может существовать, значит и не должно выделятся память для хранения второго значения поля x, но мы видим обратное.
      Вот этот вывод вообще не осилил. Как так "не должно выделятся память для хранения второго значения поля"?

      Добавлено
      Цитата riden @
      В полностью ООП языках, например в Java, попросту нельзя определять поля в наследниках, присутствующие в базовых классах, иначе возникает ошибка при компиляции.
      Боюсь огорчить, но Java не полностью ООП язык. В нём ООП даже меньше, чем в C++. С++, впрочем, тоже далеко до полностью ООП языка. Но да:
      Цитата riden @
      2) Это фича языка, или это особенность конкретной реализации, баг?
      много расставляет по своим местам. У этих языков разная объектная модель. Хотя и похожая. В Джаве нет множественного наследования реализаций, в C++ есть. Потому что авторы Джавы решили, что сложность множественного наследования слишком часто не осилима программерами и решили их оградить от ошибок. В Джаве есть интерфейсы, в C++ нет. Потому что разработчики C++ решили, что абстрактные классы являются более общим понятием, включающим интерфейсы в частном случае, но не наоборот, поэтому просто представили в языке более общий аспект.

      Добавлено
      P.S. За сим пока умолкаю до более конкретных вопросов, бо и так много получилось, на один-то пост.
      Сообщение отредактировано: Qraizer -
        Цитата Qraizer
        Цитата riden
        Цитата riden @ Вчера, 17:49
        ...хотя объекта sprite в принципе не может существовать, значит и не должно выделятся память для хранения второго значения поля x, но мы видим обратное.

        Вот этот вывод вообще не осилил. Как так "не должно выделятся память для хранения второго значения поля"?

        Я привык считать, что в ООП поле (свойство объекта) храниться только в объекте, то есть в экземпляре класса. Для объекта выделяется память. Здесь же мы видим, что объект один, а значения "поля" x - два. Получается, что 'x' - это просто переменные, а 'sprite->' и 'same->' - это области видимости к этим двум переменным. Я правильно понимаю? А разве это не идет в разрез с понятиями ООП? Вы ответили, что это фича языка. Значит я об этом смогу прочитать где-нибудь в стандарте, так?

        Цитата Qraizer
        Цитата riden
        Цитата riden @ Вчера, 17:49
        Ведь базовый класс может быть абстрактным, экземпляр которого нельзя создавать, но его поле будет все равно доступно и иметь свои значения, как-будто экземпляр этого абстрактного класса создан.

        Можно создавать экземпляры абстрактных классов. Но – только в составе других объектов как их подобъекты. Секундочку, а как иначе-то? Если их экземпляры вообще нельзя создать никак, зачем они тогда вообще нужны-то? А значит поля абстрактных классов обязательно будут в окаймляющем объекте.

        Я в курсе для чего существуют абстрактные классы. Если при наследовании от абстрактного класса в наследнике определены все чистые виртуальные функции, то наследник уже не будет являться абстрактным классом и экземпляр наследника уже можно создать. Этот процесс нельзя назвать "созданием экземпляра абстрактного класса". Далее через указатели или ссылки на абстрактные классы могут вызываться методы любых наследников этих абстрактных классов поздним связыванием. Вот для этого в основном они и существуют.

        Цитата Qraizer
        Цитата riden
        Цитата riden @ Вчера, 17:49
        В полностью ООП языках, например в Java, попросту нельзя определять поля в наследниках, присутствующие в базовых классах, иначе возникает ошибка при компиляции.

        Боюсь огорчить, но Java не полностью ООП язык. В нём ООП даже меньше, чем в C++. С++, впрочем, тоже далеко до полностью ООП языка.

        Я имел ввиду, что кроме парадигмы ООП в Java больше нет других парадигм, кроме сопутствующих (например, обобщенное программирование). А C++ - это мультипарадигменный язык.

        Цитата Qraizer
        Цитата riden
        Цитата riden @ Вчера, 17:49
        Если убрать определение поля "double x = 999" в классе SameText (оно не нужно, так как поле наследуется) то возникнет ошибка "Error C2385 ambiguous access of 'x'".

        Естественно. Их же там два. Один из this->Text::Sprite (левый предок), второй из this->SameSprite::Sprite (правый предок). Тебе какой надо менять?

        По идее, значение этого поля должно копироваться от двух предков в ОБЪЕКТ наследника, при создании экземпляра класса SameText. Тут конечно возникает конфликт: какое именно поле копировать. Я бы просто предусмотрел механизм, чтобы пользователь определял, из какого класса значение поля будет копироваться, при множественном наследовании. Далее поле существует в объекте, можно записывать значение в объект и читать значение из объекта, и никаких конфликтов уже не возникает. Кроме того, возможна ситуация, что мне просто безразлично из какого именно предка будет скопировано значение. Но и такое решение не предусмотрено.
        Сообщение отредактировано: riden -
          Тогда давай сначала об общих вопросах.
          Цитата riden @
          Я в курсе для чего существуют абстрактные классы. Если при наследовании от абстрактного класса в наследнике определены все чистые виртуальные функции, то наследник уже не будет являться абстрактным классом и экземпляр наследника уже можно создать.
          Ты описал правила их использования, но это не "для чего они существуют", а "как их можно использовать". Вопрос же в другом: зачем они вообще нужны. Мотивация в общем-то одна: если класс неспособен решать поставленную задачу по причине неконкретности реализации некоторых её аспектов, то будет лучше, если экземпляры такого класса просто нельзя будет создать без того, чтобы доопределить отсутствующее поведение. Так абстрактные классы в Плюсах и появились: вместо того, чтобы определять какое-то поведение по умолчанию, которое вообще-то и не нужно, его просто не определяют вообще, и компилятору об этом сообщают, чтобы он не пытался рассматривать это как ошибку дизайна класса, а вместо этого ошибкой считал отсутствие доопределения при его использовании.
          Но это не означает, что абстрактные классы вообще не должны иметь никакого поведения. Ведь неопределённым может быть только какой-то конкретный аспект, а все остальные вполне себе определены. Например, если класс выполняет вывод данных в поток ввода/вывода, который заранее неизвестно как связывается с физическими носителями, то неизвестны только операции вывода в поток, но форматирование от этих операций не зависит и вполне может быть реализовано. Тогда производный от него класс доопределит буквально один метод, и на этом всё, при этом ни о каком форматировании ему заботиться не нужно, оно у него уже есть посредством наследования от базового класса. Но ведь для того, чтобы базовый класс мог осуществлять форматирование, ему вполне может понадобиться и своё состояние, которое, кроме как в своих полях, хранить ему негде.

          Пойду чайник поставлю, писанина, чувствую, затянется.

          Добавлено
          Т.о. абстрактные классы могут иметь и состояния, и определённое поведение. Как и любой неабстрактный. Но кое-что в нём пока неопределено, и чётко указано, что именно. Если же взять крайний случай, когда в абстрактном классе ничего не определено, причём ему в этом случае и состояния не требуются, то получим интерфейс. Тот самый, хорошо знакомый джавистам, дельфистам, дотнистам... тьфу ты, дотнетчикам в смысле, итп. Как отдельную сущность в язык его вводить не стали, потому что... ну а зачем, собственно-то? Уже ж есть инструмент.
          Теперь далее. В Плюсах объектная модель утверждает, что любой класс является самодостаточным. В том смысле, что он живёт сам по себе и в ус не дует, не является ли он случайно частью чего-то большего. При этом он чётно отделён от всего вокруг своим опубликованным интерфейсом. Даже производный от него класс должен рассматривать его как чёрный ящик. Ну, формально, это необязательно, защищённые методы и даже поля ему-таки доступны, но сделать их таковыми было решением автора базового класса, а отнюдь не желанием автора производного. Если же класс не имеет таких полей, у производного просто нет выбора, приватные сущности ему недоступны.
          Что это даёт? Обоюдную выгоду. Производный класс точно ничего не сможет сломать в базовом, базовый же полностью защищён от (ненамеренно) деструктивных действий производного. Это дало возможность чётко разделять сферы ответственности между классами иерархии. Каждый класс занимается своим делом и ему коллинеарно, чем занимаются остальные классы, остальные же классы всегда уверены в его функциональности и готовности им служить, и могут не отвлекаться на его задачи, занимаясь только своими делами. Высочайшая степень структурной декомпозиции и абстрагирования. Собственно поэтому
          Цитата riden @
          Этот процесс нельзя назвать "созданием экземпляра абстрактного класса".
          неверно. Базовый класс вообще не знает, частью чего он является и является ли вообще. Он просто выполняет свою задачу. Поэтому в объектной модели Плюсов нельзя говорить, что подобъект более ёмкого объекта не является экземпляром класса. Является. Более того, если наследование публичное, то экземпляр производного класса одновременно является и экземпляром базового и может – даже должен – выступать им везде, где ожидается экземпляр того. (К слову. Это один из важнейших принципов: если ваш производный класс не может – возможно, потому, что и не должен, так задумано – выступать в качестве базового везде, где тот ожидается, то наследование не должно быть публичным. И весьма вероятно даже, что наследование тут в принципе не нужно, достаточно будет агрегации.)

          Добавлено
          Лирическое отступление.
          Цитата riden @
          Я имел ввиду, что кроме парадигмы ООП в Java больше нет других парадигм, кроме сопутствующих (например, обобщенное программирование). А C++ - это мультипарадигменный язык.
          Ну почему. Обобщённое в Джаве вполне есть, на дженериках. Не настолько укрученное, как это умеют шаблоны, но и шаблоны из-за своих свойств имеют определённые недостатки, которых лишены дженерики. Так что тут как бы квиты. Но в целом, я бы затруднился дать определение "полностью ООП языка". Таковым был бы, наверное, просто "ОП язык", т.е. объектный без ориентированности. Такие есть, и они, надо сказать, ...э-э-э, неудобны, что ли. Ну да ладно, не в терминах дело. Значит я просто не так понял. Вернёмся к баранам.
            Цитата riden @
            Я привык считать, что в ООП поле (свойство объекта) храниться только в объекте, то есть в экземпляре класса. Для объекта выделяется память. Здесь же мы видим, что объект один, а значения "поля" x - два. Получается, что 'x' - это просто переменные, а 'sprite->' и 'same->' - это области видимости к этим двум переменным. Я правильно понимаю? А разве это не идет в разрез с понятиями ООП? Вы ответили, что это фича языка. Значит я об этом смогу прочитать где-нибудь в стандарте, так?
            Не стал бить цитату на части. Бо как-то много бить получается. Следи за цветами.
            Конечно, в Стандарте всё есть. Только искать что-то конкретное не получится, оно размазано по куче разделов. И да, всё правильно. Только это не переменные, это... так, лучше об этом отдельно.
            В ООП, вообще говоря, нет понятия полей, есть понятие атрибутов. Ну пусть будет свойств, не суть. Атрибут – это и есть состояние объекта. Ну, состояние может быть сложным, включать несколько аспектов, потому атрибутов тоже может быть несколько. Что такое атрибут, сколько их таких и каков в них смысл – это понятно из интерфейса класса. Ну или из документации на него. Как бы там ни было, точно не из реализации. Конечно, проще всего реализовать хранение состояния, т.е. репрезентовать атрибуты, в полях объекта, но вообще говоря, это необязательно. Не каждый атрибут это поле, не каждое поле это атрибут. Самый простой пример – комплексные числа представляются парой вещественных, но атрибутов у них четыре: Re, Im, Ro и Fi; полей же обычно только два, а остальные два вычисляются при необходимости.
            Поэтому в терминах ООП некорректно говорить о наследовании полей, нужно о наследовании атрибутов. И в этом смысле атрибут x класса Sprite наследуется и Text, и SameSprite, и становится частью их состояния. Абсолютно точно так же класс SameText наследует атрибуты их обоих, и это будут разные атрибуты, т.к. хранят они состояния разных подобъектов, являющихся его частью.
            И вот теперь самое интересное. С позиции ООП нет никакой разницы между простым и множественным наследованием. В ООП есть просто понятие "наследование". Если новый класс наследует черты нескольких, то в этом нет ничего особенного. Ну, так получилось, так было нужно. Другое дело, что при наследовании от нескольких одинаковых баз не вполне понятно, что делать с одинаковыми атрибутами. Должны ли они разделяться или они должны совмещаться?
            Ответа на это ООП не даёт. Потому что это не вопрос корректности, это вопрос проектируемой архитектуры иерархии классов. Иногда нужно разделение, иногда совмещение. Ни то, ни другое не является правильным или неправильным вообще и всегда, но всегда правильно либо то, либо другое, и что именно в каждом конкретном случае будет правильным – это вопрос к тому, кто такую архитектуру проектирует. Язык же просто даёт ему инструмент для этого.
            К чему это я. К тому, что когда в языке нет множественного наследования реализаций, то язык не даёт программисту полноценного инструмента. Спорить о том, хорошо это или плохо – это добро пожаловать в наш раздел Холиваров. Тут же я только скажу, что сложность множественного наследования реализаций не является ни проблемой языка, ни проблемой ООП, она всего лишь отражает сложность задач, сводящихся к множественному наследованию реализаций. И уж коли задача сложная, стоит естественно ожидать и непростых её решений.

            Добавлено
            Ну и наконец.
            Цитата riden @
            По идее, значение этого поля должно копироваться от двух предков в ОБЪЕКТ наследника, при создании экземпляра класса SameText. Тут конечно возникает конфликт: какое именно поле копировать. Я бы просто предусмотрел механизм, чтобы пользователь определял, из какого класса значение поля будет копироваться, при множественном наследовании. Далее поле существует в объекте, можно записывать значение в объект и читать значение из объекта, и никаких конфликтов уже не возникает. Кроме того, возможна ситуация, что мне просто безразлично из какого именно предка будет скопировано значение. Но и такое решение не предусмотрено.
            Только тебе как автору иерархии знать, как правильно. Именно ты её проектируешь, и только ты сможешь ответить на этот вопрос. Вообще-то я ещё в первом посту задал вопрос
            Цитата Qraizer @
            Тебе какой надо менять?
            но только сейчас, по всей видимости, его смысл стал чётко выражен. Без того, чтобы ты на него ответил, я не смогу посоветовать конкретное решение в виде кодовых конструкций. Но превентивно могу уверить, что запрограмить можно любой вариант.

            Добавлено
            Напоследок замечу, что Плюсы при наследовании по умолчанию всегда разделяют атрибуты. Чтобы вместо разделения было совмещение, наследование должно быть виртуальным. Работает это, однако, только на уровне классов целиком. Т.е. нельзя пол-класса разделить, пол-класса совместить.
              riden, у тебя есть пробелы в базовых знаниях С++, но везде ты говоришь уверенно, даже если ошибаешься. А надо бы все подвергать сомнению. Пока определенные "моменты" не станут частью твоего опыта - нужно искать пруфы, проверять, и перепроверять.

              Вот примеры заблуждений:

              Цитата riden @
              Если при наследовании от абстрактного класса в наследнике определены все чистые виртуальные функции, то наследник уже не будет являться абстрактным классом и экземпляр наследника уже можно создать.

              Не правильно. Если в классе есть свои или наследуемые чистые виртуальные функции - класс является абстрактным.
              Скрытый текст

              Пример:
              ExpandedWrap disabled
                #include <iostream>
                using namespace std;
                 
                class A {
                  public:  
                    virtual void sA() = 0;
                };
                 
                class B: public A {
                  public:  
                    void sA() override { return;};
                };
                 
                class C: public B {
                  public:  
                    virtual void sC() = 0;
                };
                 
                int main() {
                  // A *a = new A();    
                  B *b = new B();  
                  // C *c = new C();
                  return 0;
                }

              Класс А - абстрактный, B - уже не абстрактный, С - снова абстрактный.

              Цитата riden @
              Вообще, даже если не рассматривать множественное наследование, я заметил странное поведение C++: если в классах-наслениках определять поля, которые присутствуют в базовых классах, тогда эти поля себя ведут просто как переменные с доступом через указатель на объект. Ведь базовый класс может быть абстрактным, экземпляр которого нельзя создавать, но его поле будет все равно доступно и иметь свои значения, как-будто экземпляр этого абстрактного класса создан.

              Такие "поля" ведут ровно так, как и прочие другие "поля". Просто при наследовании нужно понимать, что, если в коде указано имя публичной переменной или метода класса - то будет использовано последнее, что "видно". В противном случае нужно специфицировать, к чему именно нужно обратиться.
              Скрытый текст

              Пример:
              ExpandedWrap disabled
                #include <iostream>
                using namespace std;
                 
                class Abstract {
                  public:
                    int x;
                    virtual void SetX() = 0;
                    virtual void PrintX() { std:: cout << x << std::endl;}
                };
                 
                class Base: public Abstract {
                  public:
                    int x = 7;
                    void SetX() override { Abstract::x = 11; };
                    void PrintX() override { std:: cout << x << std::endl;}
                };
                 
                int main() {
                  Base *B = new Base();
                  B->SetX();
                  std::cout << " -----------------\n";
                  std::cout << B->Abstract::x << std::endl;
                  std::cout << B->Base::x << std::endl;
                  std::cout << " -----------------\n";
                  B->Abstract::PrintX();
                  B->PrintX();
                  std::cout << " -----------------\n";
                  delete B;
                  return 0;
                }

              Коменты опустил специально. Посмотри внимательно. Все очень просто.

              Если же говорить о множественном наследовании, то оно мало отличается от одиночного. Тот же способ компоновки полей, VMT. Одно исключение - при множественном наследовании возможны "дубли" классов-предков. Если дубли не нужны точно - применяют виртуальное наследование.

              Увы, не помню книгу, ЕМНИП что-то из Мейерса. Там вопросам инкапсуляции и наследования была посвящена целая глава, и разжевано все до состояния пюре.

              Добавлено
              Цитата riden @
              Кроме того, возможна ситуация, что мне просто безразлично из какого именно предка будет скопировано значение. Но и такое решение не предусмотрено.

              :wacko: аналогично ситуации, когда создаешь массив, инициализируешь его идентичными значениями, а индекс элемента выбираешь безразлично-рандомно в диапазоне. Да можно. Но это песец!
                Цитата riden @
                Я в курсе для чего существуют абстрактные классы. Если при наследовании от абстрактного класса в наследнике определены все чистые виртуальные функции, то наследник уже не будет являться абстрактным классом и экземпляр наследника уже можно создать.


                Вот вам абстрактные классы:

                C#:
                ExpandedWrap disabled
                  public interface ITest {
                   
                      void test();
                   
                  }
                   
                  public abstract class AbstractTest : ITest {
                   
                      public virtual void test() { }
                   
                  }


                Java:
                ExpandedWrap disabled
                  public interface ITest {
                   
                      void test();
                   
                  }
                   
                  public abstract class AbstractTest implements ITest {
                   
                      public void test() { }
                   
                  }


                C++:
                ExpandedWrap disabled
                  class ITest {
                      public:
                          virtual void test() = 0;
                  };
                   
                  class AbstractTest : public ITest {
                      public:
                          virtual void test() { }
                  };


                А теперь поясните, почему с ваших слов, все эти три примера - не являются абстрактными классами?

                Цитата riden @
                По идее, значение этого поля должно копироваться от двух предков в ОБЪЕКТ наследника, при создании экземпляра класса SameText.

                По идеи, то что вы делаете, является затиранием (сокрытием видимости переменной предка). Если вы это делаете, скорее всего:

                1. Класс-предок не ваш, и вы намеренно допускаете архитектурную ошибку.
                2. Вы намеренно путаете самого себя, и неоднозначно определяете имена переменных.
                Сообщение отредактировано: VisualProg -
                  Цитата VisualProg @
                  2. Вы намеренно путаете самого себя, и неоднозначно определяете имена переменных.
                  Кстати вот ещё. Почему-то апологеты противности множественного наследования забывают, что конфликт идентификаторов возникает при любом случайном их совпадении, а не только при наследовании, а если нет конфликта, то нет и неоднозначности. Это в дополнение к прошлому моему тезису о склонности множественного наследования быть сложным.
                  0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                  0 пользователей:


                  Рейтинг@Mail.ru
                  [ Script execution time: 0,0684 ]   [ 16 queries used ]   [ Generated: 28.03.24, 19:44 GMT ]