简体   繁体   English

C ++和Java中的抽象方法和重写函数

[英]abstract methods and overiding function in C++ and Java

In C++ and Java, or their respecting rules, what limits are placed on overiding abstract methods. 在C ++和Java中,或者他们尊重的规则,对覆盖抽象方法有什么限制。 Must you match the arguments or return type. 必须匹配参数或返回类型。 I usually see abstract functions implemented with only a return type and no arguments, is it up to derived class to specify the rest. 我通常看到只使用返回类型而没有参数实现的抽象函数,是由派生类来指定其余的。 How does it work exactly? 它是如何工作的?

Method overriding must have the same method signature of the parent method it's overriding, otherwise it's not called overriding. 方法重写必须 它覆盖的父方法具有相同的方法签名 ,否则不会调用覆盖。

Java : Java

public abstract class AbstractTest {

    public abstract void test() throws Exception;
}

public class ConcreteTest extends AbstractTest {

    @Override
    public void test() throws Exception {

    }
}

As you can see, ConcreteTest (which extends AbstractTest ) must override test() . 如您所见, ConcreteTest (扩展AbstractTest )必须覆盖test() They have the same method name, return types and no method parameters. 它们具有相同的方法名称,返回类型和方法参数。 The subclass can omit the exceptions thrown from the base class and throw its own Exception. 子类可以省略从基类抛出的异常并抛出自己的异常。 The subclass can also add additional (un)checked exception. 子类还可以添加其他(未)检查的异常。

As Peter Lawrey mentioned, a java interface methods are implicitly abstract method (See my SO question on Java Abstract Interface ). 正如Peter Lawrey所提到的,java接口方法是隐式抽象方法(参见我在Java抽象接口上的问题 )。

What is crucial here is that the method visibility cannot change in this case (as it's a hierarchical visibility, ie private->protected->public). 这里至关重要的是在这种情况下方法可见性不会改变(因为它是分层可见性,即private-> protected-> public)。 This is valid though: 这是有效的:

public abstract class AbstractTest {

    protected abstract void test() throws Exception;
}

public class ConcreteTest extends AbstractTest {

    @Override
    public void test() throws Exception {

    }
}

(The parent has a protected method and the subclass can override the same method and only has 2 choice for visibility: protected or public). (父类具有受保护的方法,子类可以覆盖相同的方法,只有2个可见性选择:protected或public)。

Also, Suppose you have 另外,假设你有

public class B {

}

public class D extends B {

}

public abstract class Base {

    public abstract B foo();
}

public class Derived extends Base {

    @Override
    public D foo() {
        // TODO Auto-generated method stub
        return new D();
    }

}

You will see that Derived returns a D and not a B . 您将看到Derived返回D而不是B Why is that? 这是为什么? That's because the derived class follows the same signature as the parent class and the return type of the derived class is a subtype of the return type of the parent class. 这是因为派生类遵循相同的签名父类和派生类的返回类型是subtype的父类的返回类型。

So, I can have this: 所以,我可以这样:

Base pureBase = new Derived();
B b = pureBase.foo(); //which returns class D

if (b instanceof D) {
   //sure, it is, do some other logic
}

In C++, you can get similar effect, using Covariant Return types 在C ++中,使用Covariant Return类型可以获得类似的效果

C++ C ++

class AbstractTest {
public:
    virtual void test() = 0;
};


class ConcreteTest : AbstractTest {
public:
    void test() {
        //Implementation here...
    }
};

In C++, a class with a pure virtual function (a virtual function that ends with a =0 ) is known as an Abstract class. 在C ++中,具有纯虚函数的类(以a =0结尾的虚函数)称为抽象类。 The subclass (in C++, class extension is delimited by : ) override the pure virtual method (except it doesn't contain the =0 ). 子类(在C ++中,类扩展由:分隔:)覆盖纯虚方法(除了它不包含=0 )。 It has the same signature has its parent class. 它具有与其父类相同的签名。

Going back to our Java example, suppose you have: 回到我们的Java示例,假设您有:

class B {

};

class D : B {

};

class Base {
public:
    virtual B* foo() = 0;
}

class Derived : Base {
public:
    D* foo() {
        return new D();
    }
}

The same reasoning (as explained in java) is done here. 这里完成了相同的推理(如java中所述)。 Covariant return types also works with protected and private inheritance. 协变返回类型也适用于受保护和私有继承。 More on Covariant return types . 有关Covariant返回类型的更多信息。

I don't know about Java, but in C++ you have to specify the exact same argument types. 我不了解Java,但在C ++中你必须指定完全相同的参数类型。 The return type -on the other hand- is a covariant type, which means that if a pointer or reference to a type A is returned in the original function, an overriding function can return a pointer or reference to a type B as long as B is either A, or derives directly or indirectly from it. 返回类型 - 另一方面 - 是协变类型,这意味着如果在原始函数中返回指向类型A的指针或引用,则覆盖函数可以返回指针或对类型B的引用,只要B是A,或直接或间接来自它。

As pointed by Als, a function must have been declared virtual in order to be overrided. 正如Als所指出的,必须将函数声明为虚拟以便被覆盖。 Since the OP explicitly asked about abstract methods, which are defined both virtual and =0 , there should be no need to point this out. 由于OP明确询问了抽象方法,它们被定义为virtual=0 ,因此不需要指出这一点。 However, I want to make extra clear that the overriding function does not need to be declared virtual. 但是,我想更清楚地表明,覆盖函数不需要声明为虚拟。 As the cited standard says, a member function matching the signature (with relaxed rules, as for covariant types) of a base member function declared virtual will be an override regardless of whether it is specified virtual or not. 正如所引用的标准所述,与声明为virtual的基本成员函数的签名(使用宽松规则,对于协变类型)匹配的成员函数将是一个覆盖,无论它是否被指定为虚拟。 That is, overriding functions do not need to be declared virtual; 也就是说,覆盖函数不需要声明为虚拟; abstract member functions, on the other hand, must be. 另一方面,抽象成员函数必须是。

Both languages are similar with respect to the requirements on overriding with the differences in semantics that are natural. 两种语言在覆盖自然语义差异的要求方面是相似的。 Basically both require the exact same constraints on calling code (ie arguments) and offer the same or more strict guarantees on processing. 基本上两者都需要对调用代码(即参数)完全相同的约束,并在处理时提供相同或更严格的保证。 This might sound a bit fuzzy here, but if you keep that in mind it is simple. 这听起来有点模糊,但如果你记住这一点很简单。

When is it an override 什么时候是覆盖

For a member function (method) to override a member of the base class both languages require the function to be polymorphic ( virtual in C++, not final in Java) have the same name and the same number an type of arguments. 对于覆盖基类成员的成员函数(方法),两种语言都要求函数是多态的(C ++中的virtual函数,而不是Java中的final函数)具有相同的名称和相同的数字作为参数类型。 Some languages allow for contra-variant argument types, but neither Java nor C++ do. 有些语言允许反变量参数类型,但Java和C ++都没有。

Covariant return type 协变返回类型

Covariant here means that the type of the return type changes in the same way that the types on which the member function is implemented. 此处的协变意味着返回类型的类型更改方式与实现成员函数的类型相同。 That is, the type returned by a derived function must be polymorphic and be the same or derived from the same type declared in the base class. 也就是说,派生函数返回的类型必须是多态的,并且与基类中声明的相同类型相同或派生。 Java is a reference language, and thus all return types can exhibit polymorphism except primitive types. Java是一种参考语言,因此除了基本类型之外,所有返回类型都可以表现出多态性。 C++ is a value language, and only references and pointers are polymorphic. C ++是一种语言,只有引用指针是多态的。 That means that in Java the returned type must match exactly or be a reference type and be derived from the type returned by the base. 这意味着在Java中,返回的类型必须完全匹配或者是引用类型,并从基类返回的类型派生。 In C++, it must be a reference or pointer to the same or a derived type. 在C ++中,它必须是指向相同或派生类型的引用指针 As in the introduction the reason is that if you call the member function through a base you will have an object that matches what you expect. 与介绍中一样,原因是如果您通过基础调用成员函数,您将拥有一个您期望的对应的对象。

Exception specifications 例外规格

Exception specifications are not very common in C++, but they are in Java. 异常规范在C ++中并不常见,但它们都是Java语言。 In both languages, though the approach to overriding is the same: the overriding method in the derived class must have tighter constraints as to what can be thrown. 在这两种语言中,尽管覆盖的方法是相同的:派生类中的重写方法必须对可以抛出的内容有更严格的约束。 The differences in the language surface here, as Java only verifies checked exceptions, so it will allow unchecked exceptions in the derived types that were not thrown by the base. 这里语言表面的差异,因为Java只验证已检查的异常,因此它将允许未被基类抛出的派生类型中的未经检查的异常 On the other hand, the deriving function cannot add new checked exceptions not present in the base class, again, covariance comes into play, and the derived function can throw covariant exceptions. 另一方面,派生函数不能添加基类中不存在的新检查异常,协方差也会发挥作用,派生函数可以抛出协变异常。 In C++ the exception specifications have a complete different meaning, but in the same way, the specification in the derived type must be more constrained than in the base and it also allows covariant exception specifications. 在C ++中,异常规范具有完全不同的含义,但是以相同的方式,派生类型中的规范必须比基础中的规范更具约束性,并且它还允许协变异常规范。

The rationale is the same, if you write a try {} catch() {} block around a call through a reference to the base type, and it catches all of the exceptions that are declared in the base, a call to the override will have all the exceptions caught in the same blocks --except possibly unchecked exceptions in Java. 基本原理是相同的,如果你通过引用基类型来编写一个try {} catch() {}块,并且它捕获了在base中声明的所有异常,对override的调用将会将所有异常捕获在相同的块中 - 除了 Java中可能未经检查的异常

Access modifiers 访问修饰符

In Java the access specification to the derived method must be at least as restrictive as that of the base, that is, if the base function declaration specifies protected , then the derived function cannot be public , but can on the other hand be private , interestingly Java does not allow you to override a private function in the base class. 在Java中,派生方法的访问规范必须至少与基类的访问规范一样,即,如果基函数声明指定protected ,那么派生函数不能是public ,但另一方面可以是private ,有趣的是Java不允许您覆盖基类中的private函数。

In C++, access specifiers don't come into play for overriding, and you can modify the access specifiers as you wish, making it more or less restrictive in the derived classes. 在C ++中,访问说明符不会用于覆盖,您可以根据需要修改访问说明符,使其在派生类中具有或多或少的限制性。 Incidentally, you can override a private member in the base class (that is declared virtual ), and that is commonly used to implement the NVI pattern (Non Virtual Interface), that has to be implemented through protected methods in Java. 顺便提一下,您可以覆盖基类中的private成员(声明为virtual ),这通常用于实现NVI模式(非虚拟接口),必须通过Java中的protected方法实现。

Stop overriding 停止压倒一切

Java allows you to break the chain of overriding at any level, by marking the member function as final or alternatively making it private . Java允许您通过将成员函数标记为final或者将其设置为private来打破任何级别的覆盖链。 In C++ (current standard) you cannot break the overriding chain at any point, not even in those cases where the final overrider does not have access to the member function it is overriding, which makes for a weird effect: 在C ++(当前标准)中,您无法在任何时候打破覆盖链,即使在最终覆盖者无法访问其覆盖的成员函数的情况下也是如此,这会产生一种奇怪的效果:

struct base {
   virtual void f() {}
};
struct derived : private base {
   void g() {
      f();
   }
};
struct most_derived : derived {
   void f() {                    // overrides base::f!!!
      //base::f();               // even if it does not have accesss to it
   } 
};

In that example, because inheritance is private at the derived level, most_derived does not have access to the base subobject, from its point of view, it does not derive from base (reason why base::f() will fail to compile inside most_derived::f() ) but on the other hand, by implementing a function with the signature void () it is providing an override for base::f . 在该示例中,因为继承在derived级别是私有的,所以most_derived无法访问base子对象,从它的角度来看,它不是从base派生的(为什么base::f()无法在most_derived::f()编译的most_derived::f() )但另一方面,通过使用签名void ()实现一个函数,它为base::f提供了一个覆盖。 A call to g() on a most_derived object will be dispatched to most_derived::f() , while on a derived object will be dispatched to base::f() . most_derived对象的g()调用将被调度到most_derived::f() ,而derived对象将被调度到base::f()

Java: Java的:

abstract class MyAbstract {
    abstract String sayHelloTo(String name);
}

final class SayEnglish extends MyAbstract {
    @Override
    public String sayHelloTo(String name) {
        return "Hello, " + name + "!";
    }
}

final class SayLatin extends MyAbstract {
    @Override
    public String sayHelloTo(String name) {
        return "Lorem, " + name + "!";
    }
}

The same is for C++ considering syntax difference, ie the same signature for overridden abstract method. 考虑到语法差异,C ++也是如此,即重写抽象方法的签名相同。

Your override methods in java should have the same signature as the abstract method you are overriding. 您在java中的覆盖方法应该与您要覆盖的抽象方法具有相同的签名。 Also you cannot restrict access more than the parent class. 此外,您不能限制访问多于父类。 See http://download.oracle.com/javase/tutorial/java/IandI/override.html 请参阅http://download.oracle.com/javase/tutorial/java/IandI/override.html

I assume that you mean C++. 我假设你的意思是C ++。 The same as java the overriding methods signature should match the overridden. 与java相同,重写方法签名应与重写匹配。 See http://www.learncpp.com/cpp-tutorial/126-pure-virtual-functions-abstract-base-classes-and-interface-classes/ 请参阅http://www.learncpp.com/cpp-tutorial/126-pure-virtual-functions-abstract-base-classes-and-interface-classes/

Wiki has a page too en.wikipedia.org/wiki/Method_overriding. Wiki也有一个页面en.wikipedia.org/wiki/Method_overriding。 Abstract methods can have parameters. 抽象方法可以有参数。 There is not restriction on that. 没有限制。 In many cases it might not make sense to pass parameters. 在许多情况下,传递参数可能没有意义。 Hope this helps :) 希望这可以帮助 :)

The signature of the method ( the return type, the type and number of arguments ) should exactly match in the derived class to that of the base class. 方法的签名(返回类型,参数的类型和数量)应该在派生类中与基类完全匹配。 Else the derived class will also become be abstract. 否则派生类也将变得抽象。

Example: 例:

struct foo{
    virtual void foobar( int myNum) = 0;
};

struct bar: foo{
    int foobar(int myNum ){}    
};

int main(){

    foo *obj = new bar();
    return 0;
}

test.cc:6: error: conflicting return type specified for 'virtual int bar::foobar(int)' test.cc:6:错误:为'virtual int bar :: foobar(int)'指定的冲突返回类型
test.cc:2: error: overriding 'virtual void foo::foobar(int)' test.cc:2:错误:覆盖'virtual void foo :: foobar(int)'

As @Als mentioned, Covariant Return Type is an exception where the return type can be different. 正如@Als所提到的, Covariant Return Type是一个例外,其中返回类型可以不同。 By different, I mean the different types should be type compatible to each. 不同的是,我的意思是不同的类型应该与每种类型兼容。 Pointer/reference of a derived class type in C++ is type compatible with pointer/reference of the base type. C ++中派生类类型的指针/引用与基类型的指针/引用类型兼容。

Example from the link : 链接示例:

#include <iostream>

// Just create a class, and a subclass
class Foo {};
class Bar : public Foo {};

class Baz
{
  public:
  virtual Foo * create()
    {
      return new Foo();
    }
};

class Quux : public Baz
{
  public:
  // Different return type, but it's allowed by the standard since Bar
  // is derived from Foo
  virtual Bar * create()
    {
      return new Bar();
    }
};

int main()
{
  Quux *tmp = new Quux();
  Bar *bar = tmp->create();

  return 0;    
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM