PDA

Показать полную графическую версию : [решено] C++: Подскажите, в чем заключается ошибка.


Oleg_SK
07-12-2009, 02:27
Решил изучить 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). Если закомментировать эти конструкторы, то прога нормально компилится и работает... Хотелось бы узнать: из-за чего возникла эта ошибка, и как ее исправить?

Oleg_SK
07-12-2009, 02:49
Все, сам разобрался - забыл объявить методы классов 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.

Oleg_SK
07-12-2009, 07:17
Так выше же написано 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) также объявлен "закрытым" (или "защищённым", не помню точно)

А вот результат создания "закрытого" деструктора я предсказать не могу :)

Oleg_SK
09-12-2009, 07:22
Кстати, забыл задать еще один вопрос по терминологии: является ли рекурсией ситуация, когда метод одного объекта класса вызывает себя в контексте другого объекта этого же класса?

Delirium
09-12-2009, 07:35
Самый простой способ ответить на вопрос - поставить точку отладки на начало функции и повызывать метод :) Если будет сам в себя заходить, то рекурсия.
Википедия - рекурсия (http://ru.wikipedia.org/wiki/Рекурсия)

Oleg_SK
09-12-2009, 08:39
Вызывать то она себя вызывает. Если бы речь шла об обычной функции, то и вопроса бы не возникло, т.к. это очевидная рекурсия. У меня с этим объектно-ориентированным программированием уже похоже скоро крышу сорвет, т.к. начал сомневаться в обычных вещах. Если не углубляться в реализацию классов, то может создаться впечатление что у каждого объекта класса имеется своя копия набора методов этого класса. Например, есть связанный список, узлами которого являются объекты одного класса. У каждого объекта этого класса есть метод Insert, который, проверив вставляемый в список узел, решает либо самостоятельно вставить этот узел, либо отправляет его дальше по списку, передавая его методу Insert следующего узла списка (так производится сортировка при вставке). В списке есть множество узлов и соответственно множество идентичных по коду методов. Таким образом создается впечатление, что лично себя метод Insert не вызывает, а вызывает свою копию в следующем узле списка, которая в свою очередь может вызвать очередную свою копию в следующем узле списка и т.д. Рекурсия - это ситуация, когда функция вызывает саму себя. В описанном же мной случае, создается впечатление, что есть множество копий одной функции, которые последовательно вызывают друг-друга... Поэтому и возник вопрос: а рекурсия ли это вообще (если не углубляться в реализацию классов компилятором)?

El Scorpio
10-12-2009, 02:37
Если не углубляться в реализацию классов, то может создаться впечатление что у каждого объекта класса имеется своя копия набора методов этого класса. »
В списке есть множество узлов и соответственно множество идентичных по коду методов. Таким образом создается впечатление, что лично себя метод Insert не вызывает, а вызывает свою копию в следующем узле списка, которая в свою очередь может вызвать очередную свою копию в следующем узле списка и т.д. »
Обычный "метод класса" - это обычная функция, которая содержит неявный параметр this (указатель на самого себя). Данный указатель используется для обращения к полям объекта и подставляется в вызовы других методов этого же объекта. А "статичный метод" (static) - это функция без параметра this. Как следствие, из неё можно обращаться только к статичным полям (которые общие для всех объектов данного класса) и вызывать другие статичные методы.

Как следствие, в рассматриваемой ситуации имеем N вложенных (рекурсивных) вызовов функции с разными значениями параметра this




© OSzone.net 2001-2012