Показать полную графическую версию : [решено] C++: Подскажите, в чем заключается ошибка.
Решил изучить C++. Сейчас разбираюсь с наследованием классов и виртуальными функциями. Слабал простенькую программку:
//---------------------------------------------------------------------------
#pragma hdrstop
#include <iostream.h>
class Mammal
{
public:
Mammal(){cout << "***\n\n";}
virtual ~Mammal(){}
virtual void Speak() const {cout << "Mammal speak!\n\n";}
//protected:
int z;
};
class Dog : public Mammal
{
Dog(){}
virtual ~Dog(){}
void Speak() const {cout << "Dog speak!\n\n";}
};
class Cat : public Mammal
{
Cat(){}
virtual ~Cat(){}
void Speak() const {cout << "Cat speak!\n\n";}
};
//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char* argv[])
{
Mammal * theArray[3];
Mammal * ptr;
int choice, i;
for (i = 0; i < 3; i++) {
cout << "(1)Dog (2)Cat: ";
cin >> choice;
switch (choice)
{
case 1:
ptr = new Dog;
break;
case 2:
ptr = new Cat;
break;
default:
ptr = new Mammal;
break;
}
if (ptr != NULL) {
theArray[i] = ptr;
}
else
{
cout << "\n\nNULL ptr!!!\n\n";
}
}
cout << "\n\n";
for (i = 0; i < 3; i++) {
theArray[i] -> Speak();
}
int x;
cin >> x;
return 0;
}
//---------------------------------------------------------------------------
Проблема заключается в том, что эта программа не компилируется; компилятор жалуется, что конструкторы классов Dog и Cat недоступны (is not accessible). Если закомментировать эти конструкторы, то прога нормально компилится и работает... Хотелось бы узнать: из-за чего возникла эта ошибка, и как ее исправить?
Все, сам разобрался - забыл объявить методы классов Cat и Dog публичными, а по дефолту они являются приватными. Непонятно только почему тогда без проблем вызывался для объектов этих классов метод Speak()? Это связано с его виртуальностью?
Delirium
07-12-2009, 03:03
class Mammal
{
public:
Mammal(){cout << "***\n\n";}
virtual ~Mammal(){}
virtual void Speak() const {cout << "Mammal speak!\n\n";} »
Так выше же написано PUBLIC: и идет перечисление публичных функций, в т.ч. и Speak. Потому и видим.
El Scorpio
07-12-2009, 03:16
Если закомментировать эти конструкторы, то прога нормально компилится и работает... »
Потому что в этом случае компилятор автоматически создаёт конструкторы, которые являются public.
Так выше же написано PUBLIC: и идет перечисление публичных функций, в т.ч. и Speak. Потому и видим. »
Непонятка в том, что без проблем вызываются и те виртуальные функции Speak(), которые определены в классах Dog и Cat, а они, из-за допущенной мной ошибки, были там приватными...
Потому что в этом случае компилятор автоматически создаёт конструкторы, которые являются public. »
То есть конструкторы и деструктор класса нужно всегда объявлять как публичные методы. Я верно понял? В книге, которую я читаю, об этом прямо не упоминается (по крайней мере, я не помню чтобы там шла об этом речь)...
El Scorpio
08-12-2009, 02:03
Oleg_SK, не содержащие какого-то специфичного кода методы "деструктор", "конструктор по умолчанию", "конструктор копирования" и "оператор присваивания" в C++ объявлять не обязательно - компилятор их создаёт и заполняет стандартным кодом автоматически.
То есть конструкторы и деструктор класса нужно всегда объявлять как публичные методы. »
Нет, не всегда.
Порою вышеперечисленные методы специально объявляют, как private, если хотят запретить данное действие в классе и его потомках. Существование "закрытого" метода в родительском классе не позволяет компилятору генерировать стандартный код в производных объектах. Данное действие будет запрещено до явного объявления открытого метода.
Явный пример - класс TObject из Borland C++ Builder. Объекты, производные от данного абстрактного класса, могут располагаться только в "куче", из-за чего операции создания объекта-копии и присваивания одного объекта другому применимы. По-этому для данного объекта явно указаны private методы TObject (const TObject &) и TObject& operator = (const TObject &).
Далее, для объектов класса TComponent - невидимый компонент экранной формы - и производных от него всегда должно быть определено значение поля "владелец" (Owner). В*результате адрес компонента-владельца указывается, как параметр конструктора, а конструктор по умолчанию TComponent (void) также объявлен "закрытым" (или "защищённым", не помню точно)
А вот результат создания "закрытого" деструктора я предсказать не могу :)
Кстати, забыл задать еще один вопрос по терминологии: является ли рекурсией ситуация, когда метод одного объекта класса вызывает себя в контексте другого объекта этого же класса?
Delirium
09-12-2009, 07:35
Самый простой способ ответить на вопрос - поставить точку отладки на начало функции и повызывать метод :) Если будет сам в себя заходить, то рекурсия.
Википедия - рекурсия (http://ru.wikipedia.org/wiki/Рекурсия)
Вызывать то она себя вызывает. Если бы речь шла об обычной функции, то и вопроса бы не возникло, т.к. это очевидная рекурсия. У меня с этим объектно-ориентированным программированием уже похоже скоро крышу сорвет, т.к. начал сомневаться в обычных вещах. Если не углубляться в реализацию классов, то может создаться впечатление что у каждого объекта класса имеется своя копия набора методов этого класса. Например, есть связанный список, узлами которого являются объекты одного класса. У каждого объекта этого класса есть метод Insert, который, проверив вставляемый в список узел, решает либо самостоятельно вставить этот узел, либо отправляет его дальше по списку, передавая его методу Insert следующего узла списка (так производится сортировка при вставке). В списке есть множество узлов и соответственно множество идентичных по коду методов. Таким образом создается впечатление, что лично себя метод Insert не вызывает, а вызывает свою копию в следующем узле списка, которая в свою очередь может вызвать очередную свою копию в следующем узле списка и т.д. Рекурсия - это ситуация, когда функция вызывает саму себя. В описанном же мной случае, создается впечатление, что есть множество копий одной функции, которые последовательно вызывают друг-друга... Поэтому и возник вопрос: а рекурсия ли это вообще (если не углубляться в реализацию классов компилятором)?
El Scorpio
10-12-2009, 02:37
Если не углубляться в реализацию классов, то может создаться впечатление что у каждого объекта класса имеется своя копия набора методов этого класса. »
В списке есть множество узлов и соответственно множество идентичных по коду методов. Таким образом создается впечатление, что лично себя метод Insert не вызывает, а вызывает свою копию в следующем узле списка, которая в свою очередь может вызвать очередную свою копию в следующем узле списка и т.д. »
Обычный "метод класса" - это обычная функция, которая содержит неявный параметр this (указатель на самого себя). Данный указатель используется для обращения к полям объекта и подставляется в вызовы других методов этого же объекта. А "статичный метод" (static) - это функция без параметра this. Как следствие, из неё можно обращаться только к статичным полям (которые общие для всех объектов данного класса) и вызывать другие статичные методы.
Как следствие, в рассматриваемой ситуации имеем N вложенных (рекурсивных) вызовов функции с разными значениями параметра this
© OSzone.net 2001-2012
vBulletin v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.