Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 06.07.2010, 03:56   #1
AJlxuMuk
 
Регистрация: 08.10.2009
Сообщений: 8
По умолчанию Наследование

proto.h

Код:
#include <iostream>
using namespace std;

class Fruit
{
	char* color;
	int size;
public:
	Fruit();
	~Fruit();
	int Set(char*);
	int Set(int);
	char* GetColor();
	int GetSize();
};

class Apple : public Fruit
{
	char* sort;
public:
	Apple();
	~Apple();
	int Set(char*);
	char* GetSort();
};

real.cpp

Код:
#include "proto.h"
#include <cstring>

int Fruit::Set(char *str)
{
	cout << "Fruit set int method" << endl;
	color = new char[25];
	strcpy(color, str);
	return 0;
};
int Fruit::Set(int Size)
{
	cout << "Fruit Set char method" << endl;
	size = Size;
	return 0;
};
char* Fruit::GetColor()
{
	return color;
};
int Fruit::GetSize()
{
	return size;
};
int Apple::Set(char *Sort)
{
	cout << "Apple Set method" << endl;
	sort = new char [100];
	strcpy(sort, Sort);
	return 0;
};
Fruit::Fruit()	
{
	std::cout << "Fruit constructor" << std::endl;
};
Fruit::~Fruit()
{
	std::cout << "Fruit destructor" << std::endl;
};
Apple::Apple()
{
	std::cout << "Apple constructor" << std::endl;
};
Apple::~Apple()
{
	std::cout << "Apple destructor" << std::endl;
};
char* Apple::GetSort()
{
	return sort;
};
main.cpp
Код:
#include "proto.h"

int main()
{
	char* sort= "richard";
	Apple homeapple;
	homeapple.Set(sort);
	return 0;
}
Вопрос: почему вызывается функция Set() класса Apple, а не его родителя? Я знаю, что если одноименную функцию родительского класса объявить виртуальной, то будет вызываться функция дочернего класса, если используем объект дочернего класса. Но в данном случае, функция Set() базового класса не объявлена виртуальной, а все равно вызывается функция из Apple. Может, это из-за того, что я перегрузил методы Set() класса Fruit. Но каким образом тогда перегрузка повлияла на механизм наследования. Помогите разобраться, пожалуйста.

Последний раз редактировалось Stilet; 06.07.2010 в 08:09.
AJlxuMuk вне форума Ответить с цитированием
Старый 06.07.2010, 05:14   #2
sever-42
Пользователь
 
Регистрация: 22.04.2010
Сообщений: 96
По умолчанию

В классе Apple есть функция Set(), она и будет вызываться.
include <Qt>
sever-42 вне форума Ответить с цитированием
Старый 06.07.2010, 06:55   #3
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,065
По умолчанию

В данном случае виртуальность метода ни на что бы не повлияла. Поведение программы изменится на то, которое Вы ожидали, если изменить код так:
Код:
int main()
{
char* sort= "richard";
Fruit *homeapple = new Apple();
homeapple->Set(sort);
delete homeapple;
return 0;
}
Метод невиртуальный, homeapple - указатель на тип Fruit. В данном случае вызовется метод класса Fruit.
Если же метод сделать виртуальным, то компилятор сначала проверит на что на самом деле указывает homeapple. Увидит, что это оказывается не Fruit, a Apple и вызовет метод из класса Apple.
pu4koff вне форума Ответить с цитированием
Старый 06.07.2010, 07:47   #4
Loji
Новичок
Джуниор
 
Регистрация: 04.07.2010
Сообщений: 38
По умолчанию

Цитата:
Сообщение от AJlxuMuk Посмотреть сообщение
proto.h

#include <iostream>
using namespace std;

class Fruit
{
char* color;
int size;
public:
Fruit();
~Fruit();
int Set(char*);
int Set(int);
char* GetColor();
int GetSize();
};

class Apple : public Fruit
{
char* sort;
public:
Apple();
~Apple();
int Set(char*);
char* GetSort();
};


real.cpp

#include "proto.h"
#include <cstring>

int Fruit::Set(char *str)
{
cout << "Fruit set int method" << endl;
color = new char[25];
strcpy(color, str);
return 0;
};
int Fruit::Set(int Size)
{
cout << "Fruit Set char method" << endl;
size = Size;
return 0;
};
char* Fruit::GetColor()
{
return color;
};
int Fruit::GetSize()
{
return size;
};
int Apple::Set(char *Sort)
{
cout << "Apple Set method" << endl;
sort = new char [100];
strcpy(sort, Sort);
return 0;
};
Fruit::Fruit()
{
std::cout << "Fruit constructor" << std::endl;
};
Fruit::~Fruit()
{
std::cout << "Fruit destructor" << std::endl;
};
Apple::Apple()
{
std::cout << "Apple constructor" << std::endl;
};
Apple::~Apple()
{
std::cout << "Apple destructor" << std::endl;
};
char* Apple::GetSort()
{
return sort;
};

main.cpp
#include "proto.h"

int main()
{
char* sort= "richard";
Apple homeapple;
homeapple.Set(sort);
return 0;
}

Вопрос: почему вызывается функция Set() класса Apple, а не его родителя? Я знаю, что если одноименную функцию родительского класса объявить виртуальной, то будет вызываться функция дочернего класса, если используем объект дочернего класса. Но в данном случае, функция Set() базового класса не объявлена виртуальной, а все равно вызывается функция из Apple. Может, это из-за того, что я перегрузил методы Set() класса Fruit. Но каким образом тогда перегрузка повлияла на механизм наследования. Помогите разобраться, пожалуйста.

Если в потомке создать метод с названием равным методу базового класса, то метод базового класса "затирается". Т.е. он как бы имеет одновременно 2 метода с одинаковыми названиями, потомка и базового класса.
Если создать указатель на Родителя, то будет создан прототип на методы родителя, а следовательно и вызов методов родителя. Вот тогда он и вызовется - "затертый" метод.
Если создать указатель на потомка, то будет создан прототип потомка и все будет ссылаться на методы потомка, а именно на твой метод, что и вызвался.
И зачем вообще создавать метод, если будет использоваться унаследованый метод родителя.
1 вариант
Цитата:
char* sort= "richard";
Apple homeapple;
Fruit fruit=(Fruit)homeapple;
fruit.Set(sort);
2 вариант, явно вызвать нужный метод базового класса
Цитата:
int Apple::Set(char *Sort)
{
Fruit::Set(Sort);
return 0;
};

Последний раз редактировалось Loji; 06.07.2010 в 07:54.
Loji вне форума Ответить с цитированием
Старый 06.07.2010, 12:43   #5
AJlxuMuk
 
Регистрация: 08.10.2009
Сообщений: 8
По умолчанию

Цитата:
Если в потомке создать метод с названием равным методу базового класса, то метод базового класса "затирается". Т.е. он как бы имеет одновременно 2 метода с одинаковыми названиями, потомка и базового класса.
Если создать указатель на Родителя, то будет создан прототип на методы родителя, а следовательно и вызов методов родителя. Вот тогда он и вызовется - "затертый" метод.
Если создать указатель на потомка, то будет создан прототип потомка и все будет ссылаться на методы потомка, а именно на твой метод, что и вызвался.
Хорошо. В чем тогда преимущество виртуальных функций? Насколько я понимаю, в том, что через указатель базового класса можно обращаться к методам потомков. А разве без виртуальных функций это делать нельзя? Впрочем, это сейчас можно проверить экспериментально...
AJlxuMuk вне форума Ответить с цитированием
Старый 06.07.2010, 12:55   #6
profi
Участник клуба Подтвердите свой е-майл
 
Регистрация: 19.11.2007
Сообщений: 1,022
По умолчанию

Цитата:
В чем тогда преимущество виртуальных функций?
Я думаю, что перед тем как задавать подобные вопросы, стоит все таки почитать литературу по С++. Советую книгу Г.Шилдта "Самоучитель С++". Просто данные вопросы показывают твою лень, что-то самому почитать и узнать.
profi вне форума Ответить с цитированием
Старый 06.07.2010, 13:29   #7
AJlxuMuk
 
Регистрация: 08.10.2009
Сообщений: 8
По умолчанию

Цитата:
Сообщение от profi Посмотреть сообщение
Я думаю, что перед тем как задавать подобные вопросы, стоит все таки почитать литературу по С++. Советую книгу Г.Шилдта "Самоучитель С++". Просто данные вопросы показывают твою лень, что-то самому почитать и узнать.
у меня она сейчас в руках. если я задал вопрос, значит, мне кое-что неясно. Прошу не указывать на личностные качества, а отвечать по существу.
AJlxuMuk вне форума Ответить с цитированием
Старый 06.07.2010, 13:48   #8
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,065
По умолчанию

Попробуйте реализовать класс "Корзинка для фруктов", в котором может храниться любое количество фруктов (на основе массива, списка,.. без разницы это). Под каждый фрукт выделим по классу. Пусть это будут классы: Зеленое яблоко, Груша, Мандарин. Ну и у фрукта создадим метод: GetColor, который будет возвращать цвет фрукта (для простоты пусть это будет целое число). Для базового класса это будет 0, для яблока - 1, для груши - 2 и для мандарина - 3. Подумайте как это будет всё работать в зависимости от виртуальности метода GetColor.
Заготовочка:
Код:
class Fruit
{
public:
  int GetColor() {return 0;}
};

class Apple: public Fruit
{
public:
  int GetColor() { return 1; }
}

// Корзинка пусть будет банальным
Fruit* fruits[10];
или
std::vector<Fruit*> fruits;
на выбор
Реализуйте перебор всех фруктов в корзинке и вывод их цвета в консоль с невиртуальным методом. Чтобы для яблока вывелся цвет яблока, а не фрукта.
pu4koff вне форума Ответить с цитированием
Старый 06.07.2010, 16:08   #9
Loji
Новичок
Джуниор
 
Регистрация: 04.07.2010
Сообщений: 38
По умолчанию

Цитата:
Сообщение от AJlxuMuk Посмотреть сообщение
Хорошо. В чем тогда преимущество виртуальных функций? Насколько я понимаю, в том, что через указатель базового класса можно обращаться к методам потомков. А разве без виртуальных функций это делать нельзя? Впрочем, это сейчас можно проверить экспериментально...
Без виртуальных функций нельзя.
Если указатель на базовый клас, то он и будет указывать только на унаследованные методы от базового класса ( че ж и придумали такое понятие как interface class ). Но, если один из них определен в потомке как виртуальный, то тогда будет вызван именно метод потомка. В этом и есть суть полиморфизма.

У тебя возник вопрос, если можно просто затереть функцию родителя в потомке, то тогда зачем использовать виртуальные функции? А суть в том, что из базового указателя ты ее никак не сможешь вызвать.

Иными словами, указатель на потомка будет всегда видеть все твои созданные методы, как виртуальные так нет, абсолютно все. Вопрос лишь стоит в том , будет ли видеть методы потомка указатель на базовый класс.

Последний раз редактировалось Loji; 06.07.2010 в 16:46.
Loji вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Наследование Сергей089 Помощь студентам 6 07.02.2010 12:04
Наследование lolopolosko Помощь студентам 1 21.11.2009 17:17
Наследование beregok Общие вопросы C/C++ 1 04.07.2009 22:39
Наследование z3rg Общие вопросы C/C++ 9 30.04.2009 20:43
Наследование Кирилл13 Общие вопросы C/C++ 1 11.11.2008 14:47