¿Cómo se declara una interfaz en C ++?


Respuestas:


686

Para ampliar la respuesta de bradtgmurray , es posible que desee hacer una excepción a la lista de métodos virtuales puros de su interfaz agregando un destructor virtual. Esto le permite pasar la propiedad del puntero a otra parte sin exponer la clase derivada concreta. El destructor no tiene que hacer nada, porque la interfaz no tiene miembros concretos. Puede parecer contradictorio definir una función tanto virtual como en línea, pero confía en mí, no lo es.

class IDemo
{
    public:
        virtual ~IDemo() {}
        virtual void OverrideMe() = 0;
};

class Parent
{
    public:
        virtual ~Parent();
};

class Child : public Parent, public IDemo
{
    public:
        virtual void OverrideMe()
        {
            //do stuff
        }
};

No tiene que incluir un cuerpo para el destructor virtual: resulta que algunos compiladores tienen problemas para optimizar un destructor vacío y es mejor usar el predeterminado.


106
Desctuctor virtual ++! Esto es muy importante. También puede incluir declaraciones virtuales puras del operador = y copiar definiciones de constructor para evitar que el compilador las genere automáticamente.
xan

33
Una alternativa a un destructor virtual es un destructor protegido. Esto desactiva la destrucción polimórfica, que puede ser más apropiada en algunas circunstancias. Busque la "Directriz # 4" en gotw.ca/publications/mill18.htm .
Fred Larson

99
Otra opción es definir un =0destructor virtual ( ) puro con un cuerpo. La ventaja aquí es que el compilador puede, en teoría, ver que vtable no tiene miembros válidos ahora y descartarlo por completo. Con un destructor virtual con un cuerpo, dicho destructor se puede llamar (virtualmente), por ejemplo, en el medio de la construcción mediante un thispuntero (cuando el objeto construido todavía es de Parenttipo), y por lo tanto el compilador debe proporcionar una tabla válida. Entonces, si no llama explícitamente a los destructores virtuales thisdurante la construcción :), puede ahorrar en el tamaño del código.
Pavel Minaev

51
Qué típico de una respuesta de C ++ que la respuesta superior no responda directamente a la pregunta (aunque obviamente el código es perfecto), sino que optimiza la respuesta simple.
Tim

18
No olvide que en C ++ 11 puede especificar la overridepalabra clave para permitir el argumento en tiempo de compilación y la comprobación del tipo de valor de retorno. Por ejemplo, en la declaración de Niñovirtual void OverrideMe() override;
Sean

245

Haz una clase con métodos virtuales puros. Use la interfaz creando otra clase que anule esos métodos virtuales.

Un método virtual puro es un método de clase que se define como virtual y se asigna a 0.

class IDemo
{
    public:
        virtual ~IDemo() {}
        virtual void OverrideMe() = 0;
};

class Child : public IDemo
{
    public:
        virtual void OverrideMe()
        {
            //do stuff
        }
};

29
debe tener un destructor de no hacer nada en IDemo para que esté definido el comportamiento que debe hacer: IDemo * p = new Child; / * lo que sea * / eliminar p;
Evan Teran el

11
¿Por qué el método OverrideMe en la clase Child es virtual? Es eso necesario ?
Cemre

99
@Cemre: no, no es necesario, pero tampoco duele.
PowerApp101

11
En general, es una buena idea mantener la palabra clave "virtual" siempre que se anule un método virtual. Aunque no es obligatorio, puede aclarar el código; de lo contrario, no tiene ninguna indicación de que ese método pueda usarse polimórficamente, o incluso exista en la clase base.
Kevin

27
@Kevin Excepto con overrideen C ++ 11
keyser

146

Toda la razón por la que tiene una categoría de tipo de interfaz especial además de las clases base abstractas en C # / Java es porque C # / Java no admite la herencia múltiple.

C ++ admite herencia múltiple, por lo que no se necesita un tipo especial. Una clase base abstracta sin métodos no abstractos (virtuales puros) es funcionalmente equivalente a una interfaz C # / Java.


17
Todavía sería bueno poder crear interfaces, para evitar que escribamos tanto (virtual, = 0, destructor virtual). También la herencia múltiple me parece una muy mala idea y nunca la he visto utilizada en la práctica, pero se necesitan interfaces todo el tiempo. Para mal, la comunidad de C ++ no introducirá interfaces solo porque las quiero.
Ha11owed

99
Ha11owed: tiene interfaces. Se llaman clases con métodos virtuales puros y sin implementaciones de métodos.
Miles Rout

66
@doc: java.lang.Thread tiene métodos y constantes que probablemente no quieras tener en tu objeto. ¿Qué debe hacer el compilador si se extiende desde Thread y otra clase con el método público checkAccess ()? ¿Realmente preferiría utilizar punteros de base fuertemente nombrados como en C ++? Esto parece un mal diseño, generalmente necesita una composición en la que cree que necesita una herencia múltiple.
Ha11owed

44
@ Ha11 reconoció que fue hace mucho tiempo, así que no recuerdo detalles, pero tenía métodos y contenidos que quería tener en mi clase y, lo que es más importante, quería que mi objeto de clase derivado fuera una Threadinstancia. La herencia múltiple puede ser un mal diseño, así como la composición. Todo depende del caso.
doc

2
@Dave: ¿En serio? ¿Objective-C tiene evaluación en tiempo de compilación y plantillas?
Deduplicador

51

No existe el concepto de "interfaz" per se en C ++. AFAIK, las interfaces se introdujeron por primera vez en Java para evitar la falta de herencia múltiple. Este concepto ha resultado ser bastante útil, y se puede lograr el mismo efecto en C ++ mediante el uso de una clase base abstracta.

Una clase base abstracta es una clase en la que al menos una función miembro (método en jerga de Java) es una función virtual pura declarada utilizando la siguiente sintaxis:

class A
{
  virtual void foo() = 0;
};

No se puede crear una instancia de una clase base abstracta, es decir, no puede declarar un objeto de la clase A. Solo puede derivar clases de A, pero cualquier clase derivada que no proporcione una implementación de foo()también será abstracta. Para dejar de ser abstracto, una clase derivada debe proporcionar implementaciones para todas las funciones virtuales puras que hereda.

Tenga en cuenta que una clase base abstracta puede ser más que una interfaz, ya que puede contener miembros de datos y funciones de miembros que no son virtuales. Un equivalente de una interfaz sería una clase base abstracta sin ningún dato con solo funciones virtuales puras.

Y, como Mark Ransom señaló, una clase base abstracta debería proporcionar un destructor virtual, al igual que cualquier clase base, para el caso.


13
Más que "falta de herencia múltiple", diría, para reemplazar la herencia múltiple. Java fue diseñado así desde el principio porque la herencia múltiple crea más problemas de los que resuelve. Buena respuesta
OscarRyz

11
Oscar, eso depende de si eres un programador de C ++ que aprendió Java o viceversa. :) En mi humilde opinión, si se usa con prudencia, como casi cualquier cosa en C ++, la herencia múltiple resuelve problemas. Una clase base abstracta de "interfaz" es un ejemplo de un uso muy juicioso de la herencia múltiple.
Dima

8
@OscarRyz Incorrecto. MI solo crea un problema cuando se usa mal. La mayoría de los supuestos problemas con MI también surgirían con diseños alternativos (sin MI). Cuando las personas tienen un problema con su diseño con MI, es culpa de MI; Si tienen un problema de diseño con SI, es su culpa. El "diamante de la muerte" (herencia repetida) es un excelente ejemplo. El ataque de MI no es pura hipocresía, sino cercano.
curioso

44
Semánticamente, las interfaces son diferentes de las clases abstractas, por lo que las interfaces de Java no son solo una solución técnica. La elección entre definir una interfaz o una clase abstracta se basa en la semántica, no en consideraciones técnicas. Imaginemos alguna interfaz "HasEngine": ese es un aspecto, una característica, y puede aplicarse / implementarse con tipos muy diferentes (ya sean clases o clases abstractas), por lo que definiremos una interfaz para eso, no una clase abstracta.
Marek Stanley

2
@MarekStanley, puede que tengas razón, pero desearía que hubieras escogido un mejor ejemplo. Me gusta pensar en términos de heredar una interfaz en lugar de heredar una implementación. En C ++, puede heredar la interfaz y la implementación juntas (herencia pública) o puede heredar solo la implementación (herencia privada). En Java tiene la opción de heredar solo la interfaz, sin una implementación.
Dima

43

Hasta donde pude probar, es muy importante agregar el destructor virtual. Estoy usando objetos creados con newy destruidos con delete.

Si no agrega el destructor virtual en la interfaz, no se llama al destructor de la clase heredada.

class IBase {
public:
    virtual ~IBase() {}; // destructor, use it to call destructor of the inherit classes
    virtual void Describe() = 0; // pure virtual method
};

class Tester : public IBase {
public:
    Tester(std::string name);
    virtual ~Tester();
    virtual void Describe();
private:
    std::string privatename;
};

Tester::Tester(std::string name) {
    std::cout << "Tester constructor" << std::endl;
    this->privatename = name;
}

Tester::~Tester() {
    std::cout << "Tester destructor" << std::endl;
}

void Tester::Describe() {
    std::cout << "I'm Tester [" << this->privatename << "]" << std::endl;
}


void descriptor(IBase * obj) {
    obj->Describe();
}

int main(int argc, char** argv) {

    std::cout << std::endl << "Tester Testing..." << std::endl;
    Tester * obj1 = new Tester("Declared with Tester");
    descriptor(obj1);
    delete obj1;

    std::cout << std::endl << "IBase Testing..." << std::endl;
    IBase * obj2 = new Tester("Declared with IBase");
    descriptor(obj2);
    delete obj2;

    // this is a bad usage of the object since it is created with "new" but there are no "delete"
    std::cout << std::endl << "Tester not defined..." << std::endl;
    descriptor(new Tester("Not defined"));


    return 0;
}

Si ejecuta el código anterior sin virtual ~IBase() {}; , verá que Tester::~Tester()nunca se llama al destructor .


3
La mejor respuesta en esta página, ya que hace el punto al proporcionar un ejemplo práctico y compilable. ¡Salud!
Lumi

1
Testet :: ~ Tester () se ejecuta solo cuando el obj es "Declarado con Tester".
Alessandro L.

En realidad, se llamará al destructor de la cadena privatename, y en la memoria, eso es todo lo que se asignará. En cuanto al tiempo de ejecución, cuando todos los miembros concretos de una clase se destruyen, también lo es la instancia de clase. Intenté un experimento similar con una clase Line que tenía dos estructuras Point y descubrí que ambas estructuras fueron destruidas (¡Ha!) Al eliminar una llamada o al regresar de la función que lo abarca. valgrind confirmó 0 fugas.
Chris Reid

33

Mi respuesta es básicamente la misma que las otras, pero creo que hay otras dos cosas importantes que hacer:

  1. Declare un destructor virtual en su interfaz o cree uno no virtual protegido para evitar comportamientos indefinidos si alguien intenta eliminar un objeto de tipo IDemo.

  2. Use la herencia virtual para evitar problemas con la herencia múltiple. (Con mayor frecuencia hay herencia múltiple cuando usamos interfaces).

Y como otras respuestas:

  • Haz una clase con métodos virtuales puros.
  • Use la interfaz creando otra clase que anule esos métodos virtuales.

    class IDemo
    {
        public:
            virtual void OverrideMe() = 0;
            virtual ~IDemo() {}
    }

    O

    class IDemo
    {
        public:
            virtual void OverrideMe() = 0;
        protected:
            ~IDemo() {}
    }

    Y

    class Child : virtual public IDemo
    {
        public:
            virtual void OverrideMe()
            {
                //do stuff
            }
    }

2
no hay necesidad de herencia virtual ya que no tiene miembros de datos en una interfaz.
Robocide

3
La herencia virtual también es importante para los métodos. Sin él, te encontrarás con ambigüedades con OverrideMe (), incluso si una de las 'instancias' es puramente virtual (solo lo intenté yo mismo).
Knarf Navillus

55
@Avishay_ " no hay necesidad de herencia virtual ya que no tiene ningún miembro de datos en una interfaz ". Incorrecto.
curioso

Tenga en cuenta que la herencia virtual puede no funcionar en algunas versiones de gcc, como la versión 4.3.3 que se entrega con WinAVR 2010: gcc.gnu.org/bugzilla/show_bug.cgi?id=35067
mMontu

-1 por tener un destructor no virtual protegido, lo siento
Wolf

10

En C ++ 11 puede evitar fácilmente la herencia por completo:

struct Interface {
  explicit Interface(SomeType& other)
  : foo([=](){ return other.my_foo(); }), 
    bar([=](){ return other.my_bar(); }), /*...*/ {}
  explicit Interface(SomeOtherType& other)
  : foo([=](){ return other.some_foo(); }), 
    bar([=](){ return other.some_bar(); }), /*...*/ {}
  // you can add more types here...

  // or use a generic constructor:
  template<class T>
  explicit Interface(T& other)
  : foo([=](){ return other.foo(); }), 
    bar([=](){ return other.bar(); }), /*...*/ {}

  const std::function<void(std::string)> foo;
  const std::function<void(std::string)> bar;
  // ...
};

En este caso, una interfaz tiene semántica de referencia, es decir, debe asegurarse de que el objeto sobreviva a la interfaz (también es posible hacer interfaces con semántica de valor).

Este tipo de interfaces tienen sus pros y sus contras:

Finalmente, la herencia es la raíz de todo mal en el diseño de software complejo. En la semántica de valor de Sean Parent y el polimorfismo basado en conceptos (muy recomendable, se explican mejores versiones de esta técnica), se estudia el siguiente caso:

Digamos que tengo una aplicación en la que trato mis formas polimórficamente usando la MyShapeinterfaz:

struct MyShape { virtual void my_draw() = 0; };
struct Circle : MyShape { void my_draw() { /* ... */ } };
// more shapes: e.g. triangle

En su aplicación, hace lo mismo con diferentes formas utilizando la YourShapeinterfaz:

struct YourShape { virtual void your_draw() = 0; };
struct Square : YourShape { void your_draw() { /* ... */ } };
/// some more shapes here...

Ahora digamos que desea usar algunas de las formas que he desarrollado en su aplicación. Conceptualmente, nuestras formas tienen la misma interfaz, pero para que mis formas funcionen en su aplicación, necesitaría extender mis formas de la siguiente manera:

struct Circle : MyShape, YourShape { 
  void my_draw() { /*stays the same*/ };
  void your_draw() { my_draw(); }
};

Primero, modificar mis formas podría no ser posible en absoluto. Además, la herencia múltiple conduce al camino hacia el código de espagueti (imagine que viene un tercer proyecto que usa elTheirShape interfaz ... ¿qué sucede si también llaman a su función de dibujo my_draw?).

Actualización: Hay un par de nuevas referencias sobre el polimorfismo basado en la no herencia:


55
La herencia TBH es mucho más clara que esa cosa de C ++ 11, que pretende ser una interfaz, pero es más bien un pegamento para unir algunos diseños inconsistentes. El ejemplo de formas está separado de la realidad y la Circleclase es un diseño pobre. Debe usar el Adapterpatrón en tales casos. Perdón si suena un poco duro, pero trata de usar alguna biblioteca de la vida real como Qtantes de hacer juicios sobre la herencia. La herencia hace la vida mucho más fácil.
doc

2
No suena duro en absoluto. ¿Cómo se separa el ejemplo de la forma de la realidad? ¿Podría dar un ejemplo (tal vez en ideone) de arreglar Circle usando el Adapterpatrón? Estoy interesado en ver sus ventajas.
gnzlbg

OK, intentaré encajar en esta pequeña caja. En primer lugar, generalmente elige bibliotecas como "MyShape" antes de comenzar a escribir su propia aplicación, para proteger su trabajo. De lo contrario, ¿cómo podría saber Squareque ya no está allí? ¿Presciencia? Por eso está separado de la realidad. Y, en realidad, si elige confiar en la biblioteca "MyShape", puede adoptar su interfaz desde el principio. En el ejemplo de formas hay muchas tonterías (una de las cuales es que tiene dos Circleestructuras), pero el adaptador se vería así -> ideone.com/UogjWk
doc

2
No está separado de la realidad entonces. Cuando la compañía A compra a la compañía B y quiere integrar la base de código de la compañía B en la de A, usted tiene dos bases de código completamente independientes. Imagine que cada uno tiene una jerarquía de formas de diferentes tipos. No puede combinarlos fácilmente con la herencia, y agregar la compañía C y tiene un gran desastre. Creo que deberías ver esta charla: youtube.com/watch?v=0I0FD3N5cgM Mi respuesta es anterior, pero verás las similitudes. No tiene que volver a implementar todo todo el tiempo, puede proporcionar una implementación en la interfaz y elegir una función miembro si está disponible.
gnzlbg

1
He visto parte del video y esto está totalmente mal. Nunca uso dynamic_cast excepto para fines de depuración. El reparto dinámico significa que hay algo mal con su diseño y los diseños en este video están mal por diseño :). Guy incluso menciona Qt, pero incluso aquí está equivocado: ¡QLayout no hereda de QWidget ni al revés!
doc

9

Todas las buenas respuestas anteriores. Una cosa adicional que debe tener en cuenta: también puede tener un destructor virtual puro. La única diferencia es que aún necesita implementarlo.

¿Confuso?


    --- header file ----
    class foo {
    public:
      foo() {;}
      virtual ~foo() = 0;

      virtual bool overrideMe() {return false;}
    };

    ---- source ----
    foo::~foo()
    {
    }

La razón principal por la que desearía hacer esto es si desea proporcionar métodos de interfaz, como yo lo he hecho, pero hacer que la anulación sea opcional.

Para convertir la clase en una clase de interfaz se requiere un método virtual puro, pero todos sus métodos virtuales tienen implementaciones predeterminadas, por lo que el único método que queda para hacer virtual puro es el destructor.

Reimplementar un destructor en la clase derivada no es gran cosa, siempre reimplemento un destructor, virtual o no, en mis clases derivadas.


44
¿Por qué, por qué, alguien querría hacer que el dtor en este caso sea puramente virtual? ¿Cuál sería la ganancia de eso? Simplemente forzaría algo en las clases derivadas que probablemente no tengan necesidad de incluir: un dtor.
Johann Gerell

66
Actualicé mi respuesta para responder a tu pregunta. El destructor virtual puro es una forma válida de lograr (¿la única forma de lograrlo?) Una clase de interfaz donde todos los métodos tienen implementaciones predeterminadas.
Rodyland

7

Si está utilizando el compilador C ++ de Microsoft, puede hacer lo siguiente:

struct __declspec(novtable) IFoo
{
    virtual void Bar() = 0;
};

class Child : public IFoo
{
public:
    virtual void Bar() override { /* Do Something */ }
}

Me gusta este enfoque porque da como resultado un código de interfaz mucho más pequeño y el tamaño del código generado puede ser significativamente más pequeño. El uso de novtable elimina toda referencia al puntero vtable en esa clase, por lo que nunca puede crear una instancia directamente. Vea la documentación aquí - novtable .


44
No entiendo por qué usaste novtablemás de lo estándarvirtual void Bar() = 0;
Flexo

2
Es adicional a (Acabo de notar lo = 0;que falta que he agregado). Lea la documentación si no la comprende.
Mark Ingram

Lo leí sin el = 0;y asumí que era solo una forma no estándar de hacer exactamente lo mismo.
Flexo

4

Una pequeña adición a lo que está escrito allí:

Primero, asegúrese de que su destructor también sea puramente virtual

En segundo lugar, es posible que desee heredar virtualmente (en lugar de normalmente) cuando implemente, solo por buenas medidas.


Me gusta la herencia virtual porque conceptualmente significa que solo hay una instancia de la clase heredada. Es cierto que la clase aquí no tiene ningún requisito de espacio, por lo que puede ser superfluo. No he hecho MI en C ++ por un tiempo, pero ¿la herencia no virtual no complicaría la transmisión?
Uri

¿Por qué, por qué, alguien querría hacer que el dtor en este caso sea puramente virtual? ¿Cuál sería la ganancia de eso? Simplemente forzaría algo en las clases derivadas que probablemente no tengan necesidad de incluir: un dtor.
Johann Gerell

2
Si hay una situación en la que un objeto se destruiría a través de un puntero a la interfaz, usted debe asegurarse de que el destructor es virtual ...
Uri

No hay nada malo con un destructor virtual puro. No es necesario, pero no tiene nada de malo. Implementar un destructor en una clase derivada no es una carga enorme para el implementador de esa clase. Vea mi respuesta a continuación para saber por qué haría esto.
Rodyland

+1 para herencia virtual, porque con las interfaces es más probable que la clase derive la interfaz de dos o más rutas. Opto por destructores protegidos en las interfaces aunque.
doc

4

También puede considerar las clases de contrato implementadas con el NVI (Patrón de interfaz no virtual). Por ejemplo:

struct Contract1 : boost::noncopyable
{
    virtual ~Contract1();
    void f(Parameters p) {
        assert(checkFPreconditions(p)&&"Contract1::f, pre-condition failure");
        // + class invariants.
        do_f(p);
        // Check post-conditions + class invariants.
    }
private:
    virtual void do_f(Parameters p) = 0;
};
...
class Concrete : public Contract1, public Contract2
{
private:
    virtual void do_f(Parameters p); // From contract 1.
    virtual void do_g(Parameters p); // From contract 2.
};

Para otros lectores, este artículo del Dr. Dobbs "Conversaciones: virtualmente tuyo", de Jim Hyslop y Herb Sutter, explica un poco más sobre por qué uno podría querer usar el NVI.
user2067021

Y también este artículo "Virtualidad" de Herb Sutter.
user2067021

1

Todavía soy nuevo en el desarrollo de C ++. Empecé con Visual Studio (VS).

Sin embargo, nadie parece mencionar el __interfaceen VS (.NET) . Soy no muy seguro de si esto es una buena manera de declarar una interfaz. Pero parece proporcionar una aplicación adicional (mencionada en los documentos ). De modo que no tenga que especificar explícitamente el virtual TYPE Method() = 0;, ya que se convertirá automáticamente.

__interface IMyInterface {
   HRESULT CommitX();
   HRESULT get_X(BSTR* pbstrName);
};

Sin embargo, no lo uso porque me preocupa la compatibilidad de compilación multiplataforma, ya que solo está disponible en .NET.

Si alguien tiene algo interesante al respecto, compártelo. :-)

Gracias.


0

Si bien es cierto que virtuales el estándar de facto para definir una interfaz, no olvidemos el clásico patrón tipo C, que viene con un constructor en C ++:

struct IButton
{
    void (*click)(); // might be std::function(void()) if you prefer

    IButton( void (*click_)() )
    : click(click_)
    {
    }
};

// call as:
// (button.*click)();

Esto tiene la ventaja de que puede volver a vincular el tiempo de ejecución de los eventos sin tener que construir su clase nuevamente (como C ++ no tiene una sintaxis para cambiar los tipos polimórficos, esta es una solución alternativa para las clases de camaleón).

Consejos:

  • Puede heredar de esto como una clase base (se permiten tanto virtuales como no virtuales) y completar click el constructor de su descendiente.
  • Puede tener el puntero de función como protectedmiembro y tener unpublic referencia y / o captador.
  • Como se mencionó anteriormente, esto le permite cambiar la implementación en tiempo de ejecución. Por lo tanto, también es una forma de administrar el estado. Dependiendo de la cantidad de ifcambios de s vs. estado en su código, esto podría ser más rápido que switch()es o ifs (se espera un cambio de alrededor de 3-4if s, pero siempre mida primero).
  • Si elige std::function<>sobre punteros de función, es posible que pueda administrar todos los datos de sus objetos IBase. Desde este punto, puede tener esquemas de valor para IBase(por ejemplo, std::vector<IBase>funcionará). Tenga en cuenta que esto puede ser más lento dependiendo de su compilador y código STL; también que las implementaciones actuales std::function<>tienden a tener una sobrecarga en comparación con los punteros de función o incluso las funciones virtuales (esto podría cambiar en el futuro).

0

Aquí está la definición de abstract class en estándar c ++

n4687

13.4.2

Una clase abstracta es una clase que solo se puede usar como clase base de otra clase; no se pueden crear objetos de una clase abstracta excepto como subobjetos de una clase derivada de ella. Una clase es abstracta si tiene al menos una función virtual pura.


-2
class Shape 
{
public:
   // pure virtual function providing interface framework.
   virtual int getArea() = 0;
   void setWidth(int w)
   {
      width = w;
   }
   void setHeight(int h)
   {
      height = h;
   }
protected:
    int width;
    int height;
};

class Rectangle: public Shape
{
public:
    int getArea()
    { 
        return (width * height); 
    }
};
class Triangle: public Shape
{
public:
    int getArea()
    { 
        return (width * height)/2; 
    }
};

int main(void)
{
     Rectangle Rect;
     Triangle  Tri;

     Rect.setWidth(5);
     Rect.setHeight(7);

     cout << "Rectangle area: " << Rect.getArea() << endl;

     Tri.setWidth(5);
     Tri.setHeight(7);

     cout << "Triangle area: " << Tri.getArea() << endl; 

     return 0;
}

Resultado: Área del rectángulo: 35 Área del triángulo: 17

Hemos visto cómo una clase abstracta definió una interfaz en términos de getArea () y otras dos clases implementaron la misma función pero con un algoritmo diferente para calcular el área específica de la forma.


55
¡Esto no es lo que se considera una interfaz! ¡Eso es solo una clase base abstracta con un método que debe ser anulado! Las interfaces son típicamente objetos que contienen solo definiciones de métodos: un "contrato" que otras clases deben cumplir cuando implementan la interfaz.
guitarflow
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.