2012-03-23 5 views
3

Я понял, почему указатель базового класса делается для указания на объект производного класса. Но я не понимаю, почему нам нужно назначить ему объект базового класса, когда он является объектом базового класса сам по себе.Виртуальные функции - указатель базового класса

Может ли кто-нибудь объяснить это?

#include <iostream> 
using namespace std; 
class base { 
    public: 
     virtual void vfunc() { 
      cout << "This is base's vfunc().\n"; 
     } 
}; 
class derived1 : public base { 
     public: 
      void vfunc() { 
       cout << "This is derived1's vfunc().\n"; 
    } 
}; 
int main() 
{ 
    base *p, b; 
    derived1 d1; 
    // point to base 
    p = &b; 
    p->vfunc(); // access base's vfunc() 
    // point to derived1 
    p = &d1; 
    p->vfunc(); // access derived1's vfunc() 
    return 0; 
} 
+3

Что вы подразумеваете под: ** почему нам нужно назначить ему объект базового класса **, можете ли вы быть более понятным. –

+0

@Rohit: В приведенном выше примере при вызове функции-члена, почему это утверждение, p = & b, когда p сам по себе является указателем базового класса и почему бы не использовать его напрямую, например: p-> func(). Я знаю, я ошибаюсь, но мне нужно исправить это. – user980411

ответ

3

Потому что указатели сами по себе ничего не могут сделать.
Указатель должен указывать на действительный объект, чтобы вы могли его использовать.

Почему вышеупомянутое заявление?

Поэтапное объяснение, возможно, устранит ваши сомнения.

Шаг 1:

base *p; 

Создает указатель p который может хранить адрес объекта класса base. Но он не инициализирован, он указывает на любой случайный адрес в памяти.

Шаг 2:

p = &b; 

Назначает адрес с действительным base объекта к указателю p. p теперь содержит адрес этого объекта.

Шаг 3:

p->vfunc(); // access base's vfunc() 

Dererences указатель p и вызов метода vfunc() на объект, на который указывает его. i.e: b.

Если удалить Шаг 2:, Ваш код просто пытается разыменования UNINITIALIZED указатель и вызовет Undefined Behavior & скорее всего аварии.

+0

Кажется, я понимаю, что это круто **. Не могли бы вы объяснить немного больше. – user980411

+0

Спасибо. Это помогло. – user980411

+1

@ user980411: Я обновил его немного больше, чтобы быть более объяснительным. Надеюсь, что это поможет. Но на самом деле вам нужно подобрать [хорошую книгу на C++] (http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). –

1

Не обязательно называть

p = &b; 
p->vFunc(); 

вы можете напрямую позвонить

b.vFunc(); 

как даст тот же результат.

Однако, похоже, вы должны понимать силу виртуальных функций. Предположим, хотите ли вы сохранить 10 экземпляров объектов base или derived1 и вызвать функцию повторно, как вы это сделаете? или после сохранения его в массиве, если вы хотите передать его общей функции?

vase *p[4]; 

base b1; 
derived d; 

p[0] = new base(); 

p[1] = &b1; 

p[2] = new dervied1();  

p[3] = &d; 

for (int i =0 ;i <4 ;i++) 
{ 
    p[i]->vFunc(); 
} 
+0

Спасибо. Какая будет линия ** p [0] = новая база(); ** return? – user980411

+1

@ user980411 он даст вам указатель на объект базового класса – Jeeva

+0

Указатель в том смысле, будет ** new base() ** создать объект типа базы и вернуть его адрес в p [0] ... ??? – user980411

1

Я не полностью уверены, что я понимаю ваш вопрос совершенно, но типичного случая использования виртуальной функции, когда мы пишем функцию, и хочу работать с объектом базы класс или что-либо производные от него:

struct base { 
    virtual void dosomething() = 0; 
}; 

void myfunc(base *b) { 
    b->dosomething(); 
} 

Когда мы пишем myfunc, мы не знаем и не заботиться о точной идентичности объекта вовлеченного - мы просто заботимся о том, что он знает, как dosomething по команде.

Что касается написания кода, как вы показали, где напрямую назначает адреса объектов базового или производного класса указателю, это скорее исключение, чем правило. Вы совершенно правы, что в подобной ситуации мы не очень много получаем от использования указателя на базу для ссылки на объект производного. Основное преимущество заключается в чем-то вроде функции, где производный класс может даже не существовать, когда мы пишем код, но до тех пор, пока производный класс будет соответствовать заданному интерфейсу, он будет работать.

+0

Спасибо за ваши искренние усилия. – user980411

-2

Этот тип ссылок очень хорошо применяется, когда вы хотите применять шаблоны проектирования (вам может потребоваться пройти продвинутый курс объектно-ориентированного дизайна или начать с чтения книги: сначала голова, дизайн-паттерны, я предлагаю)

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

public abstract class Beverage { 
String description = "Unknown Beverage"; 
public String getDescription() { 
    return description; 
} 
public abstract double cost(); 
} 

то, что мы имеем эспрессо и DarkRoast как два дочерних классов:

public class Espresso extends Beverage { 

public Espresso() { 
    this.description = "Espresso"; 
} 

public double cost() { 
    return 1.99; 
} 
} 

public class DarkRoast extends Beverage { 
public DarkRoast() { 
    description = "Dark Roast Coffee"; 
} 

public double cost() { 
    return .99; 
} 
} 

теперь мы собираемся добавить декоратор:

public abstract class CondimentDecorator extends Beverage { 
public abstract String getDescription(); 
} 

и построить некоторые конкретные декоратора:

public class Mocha extends CondimentDecorator { 

Beverage beverage; 

public Mocha(Beverage beverage) { 
    this.beverage = beverage; 
} 

public String getDescription() { 
    return beverage.getDescription() + ", Mocha"; 
} 

public double cost() { 

    return .20 + beverage.cost(); 
} 
} 

и другая обертка:

public class Whip extends CondimentDecorator { 
Beverage beverage; 

public Whip(Beverage beverage) { 
    this.beverage = beverage; 
} 

public String getDescription() { 
    return beverage.getDescription() + ", Whip"; 
} 

public double cost() { 
    return .10 + beverage.cost(); 
} 
} 

наконец, увидеть то, что произошло в основной функции и как воспользоваться преимуществами указателя на класс отца:

public static void main(String[] args) { 
    Beverage beverage = new Espresso(); 
    System.out.println(beverage.getDescription() + " $" + beverage.cost()); 
    Beverage beverage2 = new DarkRoast(); 
    beverage2 = new Mocha(beverage2); 
    beverage2 = new Mocha(beverage2); 
    beverage2 = new Whip(beverage2); 
    System.out.println(beverage2.getDescription() + " $" + beverage2.cost()); 

вы можете догадаться, что это выход? скважина:

Espresso $1.99 
Dark Roast Coffee, Mocha, Mocha, Whip $1.49 
+1

Почему вы отвечаете на вопрос C++ с кодом Java? –

Смежные вопросы