纯虚函数问题
Pure virtual function issue
我有一个基础 class,其中我有一个纯虚函数,并且有了这个函数,我想在其他派生的 classes 中覆盖它(在一些具有不同数字的那些参数(如果可能))。
所以在 MergeSort subclass 中,我有 MSort 方法,它需要不同数量的参数,因为它是递归完成的。
所以现在使用这些参数使用这个函数我收到了这个错误
'MergeSort:' 无法实例化抽象 class。但是,如果我重写基础 class 中的 Sort 方法,效果很好,但我不需要一个参数。
我还尝试用不同数量的参数声明另一个虚函数并在 MergeSort 中定义它 class 我得到了同样的结果。
我还想澄清一下,我还有其他用于不同算法(冒泡排序、插入排序等)的子classes,它们的实现类似于 MergeSort(一个构造函数和一个排序函数),但是sort 函数具有与上面的基础 class 相同的参数数量(仅一个用于图形界面)。
那么是否可以使用不同数量的参数来重写方法?或者我上面所说的任何其他解决方案?
// BASE CLASS
// Forward declaration
class Interface;
/**
* Base class from which the sorting algorithms classes will inherit (Polymorphic class)
* The base class will allow us to create a sequence with n elements
*/
class SortingAlgorithms
{
protected:
std::vector<sf::RectangleShape> sequence; // vector which will contain a randomized sequence
std::vector<sf::RectangleShape> sequenceCpy; // a copy of sequence used for interaction features
sf::RenderWindow& window; // initializes the window
int minimum, maximum; // the range in which the elements will be randomized
int elements; // the number of elements which will be initialized
public:
SortingAlgorithms();
/** SortingAlgorithms() - class constructor which initializes the sequence
* @param min - the minimum value for randomizing
* @param max - the maximum value for randomizing
* @param els - the number of elements to generate
* @param win - since the window will be initialized only once (singleton pattern);
* it will be needed to pass on this object to almost every function that has
graphics features
*/
SortingAlgorithms(int min, int max, int els, sf::RenderWindow& win);
// A pure virtual function for overriding and param init which is what I described about win param from SortingAlgorithms constructor
virtual void Sort(std::unique_ptr<Interface>& init) = 0;
};
class MergeSort : public SortingAlgorithms
{
public:
MergeSort(int min, int max, int els, sf::RenderWindow& win);
void Merge(std::unique_ptr<Interface>& init, int first, int mid, int last);
void MSort(std::unique_ptr<Interface>& init, int first, int last);
};
如评论中所述,您必须对所有覆盖使用相同的签名。为此,您可以使用以下方法:
使用覆盖函数作为一种入口点,在其中调用执行排序的真实函数(可能是私有函数)。举例说明该方法:
class SortingAlgo
{
public:
virtual void sort(int arr[], int n) = 0;
};
class BubbleSort: public SortingAlgo
{
public:
void sort(int arr[], int n){
this->bubble_sort(arr, n);
}
private:
void bubble_sort(int arr[], int n){
//implemetation
}
};
class MergeSort: public SortingAlgo
{
public:
void sort(int arr[], int n){
this->mergeSort(arr, 0, n - 1);
}
private:
void mergeSort(int arr[], int l, int r){
//recursive implemetation
}
void merge(int arr[], int l, int m, int r){
//implemenation
}
};
这是解决重载具有不同数量参数的虚拟方法问题的另一种方法。如果 parameter-types
是同一类型,这将起作用。如果类型不同,那么您可能必须应用 templates
并使用 template-type deduction
或使用 variadic function templates
...
这里是示例代码和用GCC 10.2
编译生成的程序集,可以在Compiler Explorer.
上看到
CPP
class Base {
public:
virtual void foo(int a, int b, int c, int d) = 0;
};
class Bar : public Base {
public:
// Requires All 4 Argumements - Don't Deafult any Parameter
virtual void foo(int a, int b, int c, int d) override {
// Use every parameter for this version of the function
}
};
class Baz : public Base {
public:
// Requires 3 Arguments - Default the Last Parameter
virtual void foo(int a, int b, int c, int d = 0) override {
// Use the first three parameters within this version of the function.
}
};
class Fiz : public Base {
public:
// Requires 2 Arguments - Default the Last 2 Parameters
virtual void foo(int a, int b = 0, int c = 0, int d = 0) override {
// Use the first two parameters within this version of the function.
}
};
class Buz : public Base {
public:
// Requires Only 1 Agument - Default All but the 1st Parameter
virtual void foo(int a, int b = 0, int c = 0, int d = 0) override {
// Use only the first parameter within this version of the function.
}
};
int main() {
Bar bar;
bar.foo(1,2,3,4);
Baz baz;
baz.foo(5,6,7);
Fiz fiz;
fiz.foo(8,9);
Buz buz;
buz.foo(10);
return 0;
}
ASM
Bar::foo(int, int, int, int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov DWORD PTR [rbp-16], edx
mov DWORD PTR [rbp-20], ecx
mov DWORD PTR [rbp-24], r8d
nop
pop rbp
ret
Baz::foo(int, int, int, int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov DWORD PTR [rbp-16], edx
mov DWORD PTR [rbp-20], ecx
mov DWORD PTR [rbp-24], r8d
nop
pop rbp
ret
Fiz::foo(int, int, int, int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov DWORD PTR [rbp-16], edx
mov DWORD PTR [rbp-20], ecx
mov DWORD PTR [rbp-24], r8d
nop
pop rbp
ret
Buz::foo(int, int, int, int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov DWORD PTR [rbp-16], edx
mov DWORD PTR [rbp-20], ecx
mov DWORD PTR [rbp-24], r8d
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 32
mov eax, OFFSET FLAT:vtable for Bar+16
mov QWORD PTR [rbp-8], rax
lea rax, [rbp-8]
mov r8d, 4
mov ecx, 3
mov edx, 2
mov esi, 1
mov rdi, rax
call Bar::foo(int, int, int, int)
mov eax, OFFSET FLAT:vtable for Baz+16
mov QWORD PTR [rbp-16], rax
lea rax, [rbp-16]
mov r8d, 0
mov ecx, 7
mov edx, 6
mov esi, 5
mov rdi, rax
call Baz::foo(int, int, int, int)
mov eax, OFFSET FLAT:vtable for Fiz+16
mov QWORD PTR [rbp-24], rax
lea rax, [rbp-24]
mov r8d, 0
mov ecx, 0
mov edx, 9
mov esi, 8
mov rdi, rax
call Fiz::foo(int, int, int, int)
mov eax, OFFSET FLAT:vtable for Buz+16
mov QWORD PTR [rbp-32], rax
lea rax, [rbp-32]
mov r8d, 0
mov ecx, 0
mov edx, 0
mov esi, 10
mov rdi, rax
call Buz::foo(int, int, int, int)
mov eax, 0
leave
ret
vtable for Buz:
.quad 0
.quad typeinfo for Buz
.quad Buz::foo(int, int, int, int)
vtable for Fiz:
.quad 0
.quad typeinfo for Fiz
.quad Fiz::foo(int, int, int, int)
vtable for Baz:
.quad 0
.quad typeinfo for Baz
.quad Baz::foo(int, int, int, int)
vtable for Bar:
.quad 0
.quad typeinfo for Bar
.quad Bar::foo(int, int, int, int)
typeinfo for Buz:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Buz
.quad typeinfo for Base
typeinfo name for Buz:
.string "3Buz"
typeinfo for Fiz:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Fiz
.quad typeinfo for Base
typeinfo name for Fiz:
.string "3Fiz"
typeinfo for Baz:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Baz
.quad typeinfo for Base
typeinfo name for Baz:
.string "3Baz"
typeinfo for Bar:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Bar
.quad typeinfo for Base
typeinfo name for Bar:
.string "3Bar"
typeinfo for Base:
.quad vtable for __cxxabiv1::__class_type_info+16
.quad typeinfo name for Base
typeinfo name for Base:
.string "4Base"
正如我已经说过的,这将适用于简单的情况,即函数 declaration-definitions 中每个 argument-parameter 位置的类型都是相同的类型。这里的技巧是采用具有最多参数的函数版本,并在抽象基 class.
中的 pure-virtual 函数的声明中使用它
现在,如果类型不同,您仍然可以这样做,但它们的位置必须匹配,例如:
class Base {
public:
virtual void foo(int a, double b, char c) = 0;
};
class Bar : public Base {
public:
virtual void foo(int a, double b, char c) override { } // uses all three
};
class Baz : public Base {
public:
virtual void foo(int a, double b, char c = '') override {} // uses only the int and double
};
class Biz : public Base {
public:
virtual void foo(int a, double = 0.0, char c = '') override{} // uses only the int
};
这应该也可以,但我还没有测试过。
我有一个基础 class,其中我有一个纯虚函数,并且有了这个函数,我想在其他派生的 classes 中覆盖它(在一些具有不同数字的那些参数(如果可能))。
所以在 MergeSort subclass 中,我有 MSort 方法,它需要不同数量的参数,因为它是递归完成的。
所以现在使用这些参数使用这个函数我收到了这个错误 'MergeSort:' 无法实例化抽象 class。但是,如果我重写基础 class 中的 Sort 方法,效果很好,但我不需要一个参数。
我还尝试用不同数量的参数声明另一个虚函数并在 MergeSort 中定义它 class 我得到了同样的结果。
我还想澄清一下,我还有其他用于不同算法(冒泡排序、插入排序等)的子classes,它们的实现类似于 MergeSort(一个构造函数和一个排序函数),但是sort 函数具有与上面的基础 class 相同的参数数量(仅一个用于图形界面)。
那么是否可以使用不同数量的参数来重写方法?或者我上面所说的任何其他解决方案?
// BASE CLASS
// Forward declaration
class Interface;
/**
* Base class from which the sorting algorithms classes will inherit (Polymorphic class)
* The base class will allow us to create a sequence with n elements
*/
class SortingAlgorithms
{
protected:
std::vector<sf::RectangleShape> sequence; // vector which will contain a randomized sequence
std::vector<sf::RectangleShape> sequenceCpy; // a copy of sequence used for interaction features
sf::RenderWindow& window; // initializes the window
int minimum, maximum; // the range in which the elements will be randomized
int elements; // the number of elements which will be initialized
public:
SortingAlgorithms();
/** SortingAlgorithms() - class constructor which initializes the sequence
* @param min - the minimum value for randomizing
* @param max - the maximum value for randomizing
* @param els - the number of elements to generate
* @param win - since the window will be initialized only once (singleton pattern);
* it will be needed to pass on this object to almost every function that has
graphics features
*/
SortingAlgorithms(int min, int max, int els, sf::RenderWindow& win);
// A pure virtual function for overriding and param init which is what I described about win param from SortingAlgorithms constructor
virtual void Sort(std::unique_ptr<Interface>& init) = 0;
};
class MergeSort : public SortingAlgorithms
{
public:
MergeSort(int min, int max, int els, sf::RenderWindow& win);
void Merge(std::unique_ptr<Interface>& init, int first, int mid, int last);
void MSort(std::unique_ptr<Interface>& init, int first, int last);
};
如评论中所述,您必须对所有覆盖使用相同的签名。为此,您可以使用以下方法: 使用覆盖函数作为一种入口点,在其中调用执行排序的真实函数(可能是私有函数)。举例说明该方法:
class SortingAlgo
{
public:
virtual void sort(int arr[], int n) = 0;
};
class BubbleSort: public SortingAlgo
{
public:
void sort(int arr[], int n){
this->bubble_sort(arr, n);
}
private:
void bubble_sort(int arr[], int n){
//implemetation
}
};
class MergeSort: public SortingAlgo
{
public:
void sort(int arr[], int n){
this->mergeSort(arr, 0, n - 1);
}
private:
void mergeSort(int arr[], int l, int r){
//recursive implemetation
}
void merge(int arr[], int l, int m, int r){
//implemenation
}
};
这是解决重载具有不同数量参数的虚拟方法问题的另一种方法。如果 parameter-types
是同一类型,这将起作用。如果类型不同,那么您可能必须应用 templates
并使用 template-type deduction
或使用 variadic function templates
...
这里是示例代码和用GCC 10.2
编译生成的程序集,可以在Compiler Explorer.
CPP
class Base {
public:
virtual void foo(int a, int b, int c, int d) = 0;
};
class Bar : public Base {
public:
// Requires All 4 Argumements - Don't Deafult any Parameter
virtual void foo(int a, int b, int c, int d) override {
// Use every parameter for this version of the function
}
};
class Baz : public Base {
public:
// Requires 3 Arguments - Default the Last Parameter
virtual void foo(int a, int b, int c, int d = 0) override {
// Use the first three parameters within this version of the function.
}
};
class Fiz : public Base {
public:
// Requires 2 Arguments - Default the Last 2 Parameters
virtual void foo(int a, int b = 0, int c = 0, int d = 0) override {
// Use the first two parameters within this version of the function.
}
};
class Buz : public Base {
public:
// Requires Only 1 Agument - Default All but the 1st Parameter
virtual void foo(int a, int b = 0, int c = 0, int d = 0) override {
// Use only the first parameter within this version of the function.
}
};
int main() {
Bar bar;
bar.foo(1,2,3,4);
Baz baz;
baz.foo(5,6,7);
Fiz fiz;
fiz.foo(8,9);
Buz buz;
buz.foo(10);
return 0;
}
ASM
Bar::foo(int, int, int, int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov DWORD PTR [rbp-16], edx
mov DWORD PTR [rbp-20], ecx
mov DWORD PTR [rbp-24], r8d
nop
pop rbp
ret
Baz::foo(int, int, int, int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov DWORD PTR [rbp-16], edx
mov DWORD PTR [rbp-20], ecx
mov DWORD PTR [rbp-24], r8d
nop
pop rbp
ret
Fiz::foo(int, int, int, int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov DWORD PTR [rbp-16], edx
mov DWORD PTR [rbp-20], ecx
mov DWORD PTR [rbp-24], r8d
nop
pop rbp
ret
Buz::foo(int, int, int, int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov DWORD PTR [rbp-16], edx
mov DWORD PTR [rbp-20], ecx
mov DWORD PTR [rbp-24], r8d
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 32
mov eax, OFFSET FLAT:vtable for Bar+16
mov QWORD PTR [rbp-8], rax
lea rax, [rbp-8]
mov r8d, 4
mov ecx, 3
mov edx, 2
mov esi, 1
mov rdi, rax
call Bar::foo(int, int, int, int)
mov eax, OFFSET FLAT:vtable for Baz+16
mov QWORD PTR [rbp-16], rax
lea rax, [rbp-16]
mov r8d, 0
mov ecx, 7
mov edx, 6
mov esi, 5
mov rdi, rax
call Baz::foo(int, int, int, int)
mov eax, OFFSET FLAT:vtable for Fiz+16
mov QWORD PTR [rbp-24], rax
lea rax, [rbp-24]
mov r8d, 0
mov ecx, 0
mov edx, 9
mov esi, 8
mov rdi, rax
call Fiz::foo(int, int, int, int)
mov eax, OFFSET FLAT:vtable for Buz+16
mov QWORD PTR [rbp-32], rax
lea rax, [rbp-32]
mov r8d, 0
mov ecx, 0
mov edx, 0
mov esi, 10
mov rdi, rax
call Buz::foo(int, int, int, int)
mov eax, 0
leave
ret
vtable for Buz:
.quad 0
.quad typeinfo for Buz
.quad Buz::foo(int, int, int, int)
vtable for Fiz:
.quad 0
.quad typeinfo for Fiz
.quad Fiz::foo(int, int, int, int)
vtable for Baz:
.quad 0
.quad typeinfo for Baz
.quad Baz::foo(int, int, int, int)
vtable for Bar:
.quad 0
.quad typeinfo for Bar
.quad Bar::foo(int, int, int, int)
typeinfo for Buz:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Buz
.quad typeinfo for Base
typeinfo name for Buz:
.string "3Buz"
typeinfo for Fiz:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Fiz
.quad typeinfo for Base
typeinfo name for Fiz:
.string "3Fiz"
typeinfo for Baz:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Baz
.quad typeinfo for Base
typeinfo name for Baz:
.string "3Baz"
typeinfo for Bar:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Bar
.quad typeinfo for Base
typeinfo name for Bar:
.string "3Bar"
typeinfo for Base:
.quad vtable for __cxxabiv1::__class_type_info+16
.quad typeinfo name for Base
typeinfo name for Base:
.string "4Base"
正如我已经说过的,这将适用于简单的情况,即函数 declaration-definitions 中每个 argument-parameter 位置的类型都是相同的类型。这里的技巧是采用具有最多参数的函数版本,并在抽象基 class.
中的 pure-virtual 函数的声明中使用它现在,如果类型不同,您仍然可以这样做,但它们的位置必须匹配,例如:
class Base {
public:
virtual void foo(int a, double b, char c) = 0;
};
class Bar : public Base {
public:
virtual void foo(int a, double b, char c) override { } // uses all three
};
class Baz : public Base {
public:
virtual void foo(int a, double b, char c = '') override {} // uses only the int and double
};
class Biz : public Base {
public:
virtual void foo(int a, double = 0.0, char c = '') override{} // uses only the int
};
这应该也可以,但我还没有测试过。