С++: наследование функций и их переопределение

У меня возникают некоторые проблемы с интерфейсами, наследование и переопределение. В этом случае я не уверен, почему именно C++ ведет себя так, поэтому, если кто-то может объяснить и помочь мне с этим. У меня есть классы:

module.h:

class mixer;

class module {

public:

 module(std::string name_) : name(name_) {}
 ~module() {}

 virtual module& bind(module &mod) = 0;
 virtual module& bind(mixer &mix) { return ((module&)mix); }

 std::string get_name() 
 {
 return name;
 }

 std::string name;
};

in_out.h:

class in_out : public module {

public:

 in_out(std::string name_) : module(name_) {}
 ~in_out() {}

 virtual module& bind(module &mod)
 {
 std::cout << "bind in_out in " << mod.name << std::endl;
 return mod;
 }

};

prod.h:

class prod : public in_out {

public:

 prod(std::string name_)
 : in_out(name_)
 {}

 ~prod() {}

 virtual module& bind(mixer &mix)
 {
 std::cout << "bind mixer in " << get_name() << std::endl;
 return mix;
 }

};

mixer.h:

class mixer : public in_out {

public:

 mixer(std::string name_)
 : in_out(name_)
 {}

 ~mixer() {}
};

Итак, если в моем основном файле у меня есть:

int main(int argc, char *argv[])
{
 prod prod1("prod1");
 prod prod2("prod2");
 mixer mixer1("mixer1");
 mixer mixer2("mixer2");

 prod1.bind(prod2);

 return 0;
}

Я получаю эту ошибку:

main.cpp: In function ‘int main(int, char**):
main.cpp:12:19: error: no matching function for call to ‘prod::bind(prod&)
main.cpp:12:19: note: candidate is:
prod.h:19:23: note: virtual pfn_module& prod::bind(mixer&)
prod.h:19:23: note: no known conversion for argument 1 from ‘prod to ‘mixer&
make: *** [main] Error 1

Если у меня есть это:

prod1.in_out::bind(prod2);

Он работает так, как ожидалось.

Я не понимаю, не должен ли компилятор отличать их?

virtual module& bind(module &mod) = 0;
// and
virtual module& bind(mixer &mix) { return ((module&)mix); }

Я думаю, проблема может заключаться в том, что и mixer и prod являются дочерними modulein_out). Может быть, когда функция bind вызывается в основном, она ищет ее в определении prod и только находит bind(mixer)? Как насчет bind(module)? Является ли он конфиденциальным в этом контексте?

Я бы хотел, чтобы независимо от того, что я называю prod.bind(prod) или prod.bind(mixer), он отличает их на уровне prod, поэтому мне не нужно вызывать .in_out оттуда.

Большое спасибо :)

2 ответа

Проблема в том, что когда вы определили класс prod и объявленную функцию bind

virtual module& bind(mixer &mix);

в области класса вы скрывали все другие функции с тем же именем базового класса. Тогда вы позвонили

prod1.bind(prod2);

компилятор видит только один кандидат в классе: функция, показанная выше. И он не может преобразовать опорный код для ссылки на миксер.

Вы должны написать в определении класса

using in_out::bind;

И как отметил @Alex в своем комментарии, вы должны сделать деструктор класса класса виртуальным, если вы используете полиморфизм.

EDIT: если подставить pfn_inout для in_out, потому что нет определения pfn_inout, то следующий код успешно компилируется

#include <iostream>
#include <string>

class mixer;

class module {

public:

 module(std::string name_) : name(name_) {}
 ~module() {}

 virtual module& bind(module &mod) = 0;
 virtual module& bind(mixer &mix) { return ((module&)mix); }

 std::string get_name() 
 {
 return name;
 }

 std::string name;
};

class in_out : public module {

public:

 in_out(std::string name_) : module(name_) {}
 ~in_out() {}

 virtual module& bind(module &mod)
 {
 std::cout << "bind in_out in " << mod.name << std::endl;
 return mod;
 }

};

class mixer : public in_out {

public:

 mixer(std::string name_)
 : in_out(name_)
 {}

 ~mixer() {}
};

class prod : public in_out {

public:
 using in_out::bind;

 prod(std::string name_)
 : in_out(name_)
 {}

 ~prod() {}

 virtual module& bind(mixer &mix)
 {
 std::cout << "bind mixer in " << get_name() << std::endl;
 return mix;
 }

};


int main() 
{
 prod prod1("prod1");
 prod prod2("prod2");
 mixer mixer1("mixer1");
 mixer mixer2("mixer2");

 prod1.bind(prod2);

 return 0;
} 
</string></iostream>


В C++ поиск имени останавливается в области, где имя найдено. (Есть некоторые исключения, связанные с ADL, но они здесь не применяются.) Причина этого заключается в том, чтобы избежать изменения семантики функций в вашей производной, если имена добавляются в базовый класс.

Как правило, это не должно быть проблемой, потому что вы должны реализовать все виртуальные функции в интерфейсе во всех производных классах. А виртуальные функции и не виртуальные функции не должны делить имена. В вашем случае то, что вы, вероятно, хотите в базовом классе, это что-то вроде:

class Module
{
private:
 virtual Module& doBind( Module& mod ) = 0;

public
 Module& bind( Module& mod )
 {
 // pre-conditions
 Module& results = doBind( mod );
 // post-conditions and invariants
 return results;
 }
 Module& bind( Mixer& mix )
 {
 // pre-conditions
 Module& results = doBind( static_cast<module&>( mod) );
 // post-conditions and invariants
 return results;
 }
};
</module&>

Производные классы будут реализовывать собственные версии doBind, но ничего больше.

Одна вещь. Преобразование (Module&)mix, в контексте, когда компилятор не может видеть полное определение Module, представляет собой reinterpret_cast, что почти наверняка не то, что вы хотите. Я использовал static_cast в моем примере, чтобы согласовать с тем, что вы сделали, но на самом деле это незаконно, если компилятор не может видеть определение Module. В этом конкретном случае на самом деле нет необходимости в том, что когда-либо было для второй функции bind, поскольку Mixer& будет неявно преобразовывать в Module&, без необходимости преобразования с вашей стороны. (Но вполне возможно, что этот код является просто упрощенным примером, и что в вашем реальном коде вы на самом деле делаете что-то более сложное.)

licensed under cc by-sa 3.0 with attribution.