我有一个关于 C++ 虚函数的问题。该virtual
关键字用于class
基类中函数的 C++ 声明中,以通知该函数的子类实现可能因子类而异。不同的子类可能有不同的功能实现。
我有点不明白。就我现在所见,当您在 C++ 中定义接口时,它不像在 Java 中那样。
事实上,我不明白 C++ 中的接口这个词是什么意思。virtual
您可以在头文件中指定一个函数。然后基类的子类或派生类可以以任何你想要的方式覆盖它,只要它是一个虚函数。
C++中的接口是头文件吗?
干杯
我有一个关于 C++ 虚函数的问题。该virtual
关键字用于class
基类中函数的 C++ 声明中,以通知该函数的子类实现可能因子类而异。不同的子类可能有不同的功能实现。
我有点不明白。就我现在所见,当您在 C++ 中定义接口时,它不像在 Java 中那样。
事实上,我不明白 C++ 中的接口这个词是什么意思。virtual
您可以在头文件中指定一个函数。然后基类的子类或派生类可以以任何你想要的方式覆盖它,只要它是一个虚函数。
C++中的接口是头文件吗?
干杯
Java 和 C++ 之间的主要区别在于,Java 中的每个函数都是virtual
.
从历史上看,当 C++ 被开发时,在每个函数调用之间放置一个抽象层(这是使用虚拟方法表发生的事情)并不是您对每个方法调用都想要的,因为虚拟方法调用较慢。
所以发生的情况是,您必须指定必须通过动态绑定调用方法,否则在编译时根据变量的声明选择方法。这意味着,如果您声明Base *b = new Derived()
并调用了一个b
不是虚拟的方法,则在编译时选择该方法,它将是一个Base::method
.
座右铭是你不为你不使用的东西付费,就是这样。
C++ 中的接口不存在,但您可以拥有一个仅具有纯虚函数的类,它们的行为方式基本相同。实际上你可以有两种虚拟方法:
class Base {
virtual void method() {
//implementation
}
virtual void pureMethod() = 0;
}
首先method()
是虚拟的并服从动态绑定,但它甚至在基类中实现,同时pureMethod()
仍然服从动态绑定,但它被声明为纯虚拟,因此它没有任何实现,因此Base
不能按原样实例化,您需要对其进行子类化并至少覆盖纯虚方法。
Java 中的接口是一种特定的语言结构或关键字。Java 有一个关键字interface
,用于指示 Java 类不提供实际实现,而是描述派生类必须实现的接口。Java 编译器会检查这一点,以确保声称实现接口的类为该接口提供了所有必要的方法。
在 Java 中,interface
关键字表示一个类提供一组由指定的接口类描述的服务。一个 Java 类可以实现多个不同的接口。
在与 C++ 的讨论中使用接口这个词的方式是一个有点相似的概念,因为它与类或函数或方法的参数类型有关。但是 C++ 中没有interface
关键字。接口这个词在 C++ 中以更通用、更具描述性的方式使用,如“函数接口有两条短线和一条长线”。它表示函数调用参数及其类型,函数调用者与函数体之间的接口。
因此,将 C++ 中的接口视为实现接口的类与使用从该类实例化的对象的任何对象之间的一种契约。一个类实际上还可以提供几个不同的相关服务,每个服务都有一个特定的接口。
interface
通过在类描述中使用纯虚方法来创建抽象类,可以做类似于 C++ 中的 Java概念的事情。这将创建一个没有实现的 C++ 类。请参阅C++:使用抽象方法创建抽象类并覆盖子类中的方法。
虚方法的作用是提供一种机制,使使用类的派生类的对象可以依赖于特定接口,同时将实现的细节留给派生类。所以这类似于Java接口。C++ 虚拟方法是一种使用编译和静态检查方法而不是运行时方法来实现 Java 接口的方法。
使用虚拟方法,您可以创建一个超类类型,该类型可用于创建指针变量,这些变量可以从派生类型中分配变量,并且当您使用虚拟方法时,将调用派生类中的正确方法。确定调用哪个方法是在编译时而不是运行时完成的。C++ 的主要思想是与 C 一样高效,同时提供面向对象的语言结构以及编译时的静态类型检查,以减少对运行时错误检测的依赖及其额外的开销。
class Joe {
public:
virtual int thingOne() { return 1;} // standard, not pure virtual method
..
};
class JoeTwo : public Joe {
public:
int thingOne() { return 2;} // derived class provides its own version of the method
..
};
Joe *myJoe = new JoeTwo;
int i = myJoe->thingOne(); // value of 2 put into i and not value of 1
使用 C++,您确实需要识别对象和指向对象的指针之间的区别,因为当将包含派生对象的变量分配给包含派生对象的超类的变量时,可能会发生对象切片。这种“对象切片”会发生,因为派生对象不适合派生它的基类或超类对象。派生对象具有超类对象所没有的附加内容,因此通过分配(默认分配是直接内存复制)仅将派生对象的超类部分复制到超类对象中。
class Joe {
public:
virtual int thingOne() { return 1;} // standard, not pure virtual method
..
};
class JoeTwo : public Joe {
public:
int thingOne() { return 2;} // derived class provides its own version of the method
..
};
Joe *myJoe = new JoeTwo; // no object slicing since this is pointer
JoeTwo myJoeTwo;
Joe myJoeSliced = myJoeTwo; // JoeTwo object myJoeTwo sliced to fit into Joe object myJoeSliced
C++没有像Java那样的接口。你得到的最接近的是当你定义一个只有纯虚函数的类时,这样从那个类派生的每个人都被迫实现所有这些。
C++ 中的虚方法是一种可以被覆盖的方法。纯虚方法(定义为virtual foo() = 0)是一种抽象方法。要创建一个类似于您在 Java 中使用的接口,您只需指定所有方法都是纯虚拟的(抽象的)。
但是,有时“接口”指的是通常完全位于标头内的类的公共成员和类型。
c++ 类的接口在头文件中定义。仅当您想要子类化并使用继承时才使用虚函数。
在 C++ 中,最接近类似 java 的接口是使用纯抽象类。实现具有此接口的类必须继承此抽象类。
不要忘记将你的解构器设为虚拟,否则在引用基类时不会调用子类的解构器。
例子:
class PuppetInterface
{
public:
virtual ~PuppetInterface() {};
virtual void walk() = 0;
virtual void tellALie() = 0;
};
class Pinocchio : public PuppetInterface
{
public:
~Pinocchio()
{
//lie down look dead.
}
void walk()
{
//try moving wooden legs.
}
void tellALie()
{
//let nose grow look serious.
}
};
int main(int argc, const char * argv[])
{
PuppetInterface* pinocchio = new Pinocchio();
pinocchio->walk();
pinocchio->tellALie();
delete pinocchio;
return 0;
}
编辑:由于原始指针不好,不能保存异常,可能会泄漏等。上面的代码应该重写为
//using c++ 11
int main(int argc, const char * argv[])
{
auto pinocchio = std::unique_ptr<PuppetInterface>(new Pinocchio());
pinocchio->walk();
pinocchio->tellALie();
}