提供纯虚函数的定义
Providing a definition for a pure-virtual function
我是一名 Java 程序员,对为纯虚函数提供定义这一事实感到困惑。在 Java 中,我习惯于将抽象方法视为我们将在基础 class 中提供定义的方法。但是下面的代码是完全有效的:
#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
仍然是一个抽象 class。而您正在尝试实例化它。
在代码的第一个版本中,唯一继承自 A
的纯函数 B
是析构函数,并且在 B
中提供了析构函数的非纯定义。因此,B
不再是抽象的 class 并且可以合法实例化。
在代码的第二个版本中 B
从 A
继承了两个纯虚函数——析构函数和 foo
。由于您没有覆盖 B
中的 foo
,B
仍然是一个抽象 class,无法实例化。
仅此而已。该错误与在命名空间范围内为纯虚函数提供主体的能力无关。
当我使用 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();
,它在基础 [=26] 中声明为纯虚函数=].
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;
}
我是一名 Java 程序员,对为纯虚函数提供定义这一事实感到困惑。在 Java 中,我习惯于将抽象方法视为我们将在基础 class 中提供定义的方法。但是下面的代码是完全有效的:
#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
仍然是一个抽象 class。而您正在尝试实例化它。
在代码的第一个版本中,唯一继承自 A
的纯函数 B
是析构函数,并且在 B
中提供了析构函数的非纯定义。因此,B
不再是抽象的 class 并且可以合法实例化。
在代码的第二个版本中 B
从 A
继承了两个纯虚函数——析构函数和 foo
。由于您没有覆盖 B
中的 foo
,B
仍然是一个抽象 class,无法实例化。
仅此而已。该错误与在命名空间范围内为纯虚函数提供主体的能力无关。
当我使用 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();
,它在基础 [=26] 中声明为纯虚函数=].
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;
}