我是一名 Java 程序员,对提供纯虚函数的定义感到困惑。在 Java 中,我习惯于将抽象方法视为我们将在基类中提供定义的方法。但下面的代码是完全有效的:
#include <iostream>
struct A
{
virtual ~A() = 0;
};
A::~A(){ std::cout << "~A()" << std::endl; }
struct B : A
{
~B(){ std::cout << "~B()" << std::endl; }
};
A *a = new B;
int main()
{
delete a;
}
但是如果我们尝试做这样的事情:
#include <iostream>
struct A
{
virtual ~A() = 0;
virtual void foo() = 0;
};
void A::foo(){ }
A::~A(){ std::cout << "~A()" << std::endl; }
struct B : A
{
~B(){ std::cout << "~B()" << std::endl; }
};
A *a = new B;
int main()
{
delete a;
}
编译器会抱怨没有提供纯虚函数的定义。为什么我们可以在命名空间作用域中定义纯虚析构函数,但对于普通的成员函数却不能这样做。
这是例外而不是规则?
您可以定义任何纯虚成员函数。但是,即使您定义了
A::foo
,它仍然是纯的,因此 A
和 B
仍然是抽象类,可能不会被实例化。这将是您的编译器可能发出的任何错误的原因。
您显然对第二版代码中的错误原因感到困惑。
在命名空间范围内为纯虚函数(任何纯虚函数)提供定义没有任何问题。它是析构函数还是常规函数并不重要。没有编译器会抱怨这一点。
您在代码的第二个版本中提供的
A::foo
的定义完全合法,不会引起编译器的投诉。第二个代码的唯一问题是 B
不会覆盖 foo
。因此 B
仍然是一个抽象类。并且您正在尝试实例化它。
在代码的第一个版本中,唯一从
B
继承的纯函数 A
是析构函数,并且在 B
中提供了析构函数的非纯定义。所以,B
不再是抽象类,可以合法实例化。
在代码的第二个版本中,
B
从A
继承了两个纯虚函数 - 析构函数和foo
。由于您没有在 foo
中重写 B
,因此 B
仍然是一个抽象类,无法实例化。
仅此而已。该错误与为命名空间范围内的纯虚函数提供主体的能力无关。
当我使用 g++ 4.8.2 编译上述程序时,我收到以下消息:
编译命令:
g++ -Wall -std=c++11 socc.cc -o socc
错误信息:
socc.cc:17:12: error: cannot allocate an object of abstract type ‘B’
A *a = new B;
^
socc.cc:12:8: note: because the following virtual functions are pure within ‘B’:
struct B : A
^
socc.cc:9:6: note: virtual void A::foo()
void A::foo(){ }
^
make: *** [socc] Error 1
错误消息的要点是
B
是一个抽象类型,因为它不提供重写 void foo();
,它在基类中声明为纯虚函数。
A::foo()
的定义并不违法。以下程序运行良好:
#include <iostream>
struct A
{
virtual ~A() = 0;
virtual void foo() = 0;
};
void A::foo(){ }
A::~A(){ std::cout << "~A()" << std::endl; }
struct B : A
{
~B(){ std::cout << "~B()" << std::endl; }
void foo() {} // Need this to be able to instantiate B.
};
A *a = new B;
int main()
{
delete a;
}