Я новичок в C++, и мне нужно уточнить перенос проекта с Java.
В Java я могу объявить базовый класс и его производные с помощью дженериков следующим образом:
public class GenericHost{
public enum HostType{
server,
client
}
public HostType type_;
}
public class MyClient extends GenericHost{
public String clientName;
}
public class MyServer extends GenericHost{
public String serverName;
}
public abstract class GenericNetwork<hostType extends GenericHost> {
public enum NetworkType{
central,
peripheral
}
private NetworkType type_;
protected hostType masterHost;
public hostType getMasterHost(){
return masterHost;
}
public abstract String getName();
}
public class CentralNetwork extends GenericNetwork<MyServer>{
@Override
public String getName(){
return masterHost.serverName;
}
}
public class PeripheralNetwork extends GenericNetwork<MyClient>{
@Override
public String getName(){
return masterHost.clientName;
}
}
Это позволяет мне:
В производных классах мне разрешено использовать методы и переменные указанного производного класса (например, serverName
/clientName
в сети CentralNetwork
/PeripheralNetwork
), а не только базового класса
Производный класс задан, поэтому компилятор/редактор может предложить мне каждый метод и переменную во время редактирования кода
Я вынужден использовать класс, который получен из базового класса (GenericNetwork
/GenericHost
), каждая ошибка во время компиляции и не запускается
Каждый метод/переменная, использующая generics, будет обрабатываться в производном классе как дочерний класс, а не базовый класс (например, в CentralNetwork
, getMasterHost
вернет полученный MyServer
, а не базовый GenericHost
).
Я хочу знать, существует ли что-либо подобное в C++. Я уже искал шаблоны, наследование и подтипирование, но я не могу найти способ сделать что-то умнее, чем в Java. Надеюсь, я что-то пропустил...
EDIT: Это то, что я пытался в C++:
class GenericHost{
public enum HostType{
server,
client
}
public HostType type_;
}
class MyClient : public GenericHost{
public String clientName;
}
class MyServer : public GenericHost{
public String serverName;
}
template<class hostType : GenericHost> <--WISH, forced base class
class GenericNetwork {
public enum NetworkType{
central,
peripheral
}
private NetworkType type_;
protected hostType masterHost;
public hostType getMasterHost(){
return masterHost; <--WISH, should return MyServer / Myclient in derived class
}
public virtual std::string getName();
}
class CentralNetwork<MyServer> : public GenericNetwork{
public std::string getName(){
return masterHost.serverName; <--WISH, tiped and suggested by editor / compiler
}
}
class PeripheralNetwork<MyClient>: public GenericNetwork{
public std::string getName(){
return masterHost.clientName; <--WISH, tiped and suggested by editor / compiler
}
}
У меня сейчас нет проекта C, поэтому я переписал его на лету, извините за любую ошибку...
Как отмечают все, шаблоны C++ могут реализовать это, поэтому он не заслуживает выделенного синтаксиса.
Здесь довольно буквальный перевод, который обеспечивает требование базового класса, просто делая это.
#include <string>
struct GenericHost {
enum HostType { server,client } type_;
};
template<class GenericHost=GenericHost>
struct MyClient : GenericHost { std::string clientName; };
template<class GenericHost=GenericHost>
struct MyServer : GenericHost { std::string serverName; };
template< template<class> class SpecificHost, class GenericHost=GenericHost >
struct GenericNetwork
{
typedef SpecificHost<GenericHost> hostType;
virtual ~GenericNetwork() { };
enum NetworkType { central, peripheral };
hostType getMasterHost() { return masterHost; }
virtual std::string getName() = 0;
protected: hostType masterHost;
private: NetworkType type_;
};
struct CentralNetwork : GenericNetwork<MyServer> {
std::string getName() { return masterHost.serverName; }
};
struct PeripheralNetwork : GenericNetwork<MyClient> {
std::string getName() { return masterHost.clientName; }
};
// testing: force instantiation:
struct CentralNetwork makeme;
struct PeripheralNetwork metoo;
std::string doit() { return makeme.getName() + metoo.getName(); }
Я считаю, что это получает все четыре желания, хотя ошибки будут обнаружены в разных местах. Как и другие, укажите static_cast<requiredBase*>((testedClass*)0);
может выполнить эту работу, но в обход защиты требуется работа, а не просто ошибка, и она появится в системе типов, поэтому я не вижу смысла.
(отредактируйте: добавьте виртуальный деструктор, не десерт для меня сегодня. Плохая собака.)
public
стандарт для структур лучше описывает общую доступность внутренностей, я использую его.
Насколько я знаю, нет явной функции, позволяющей это сделать. Вы можете использовать static_cast
хотя это даст вам ошибку времени компиляции, если типы несовместимы.
template <class hostType>
class GenericNetwork {
public:
GenericNetwork() {
static_cast<GenericHost*>((hostType*)nullptr); // or 0 or NULL if not C++11
}
};
Если hostType
и GenericHost
совместимы, бросок будет успешным, но ничего не сделает. В противном случае вы получите ошибку времени компиляции.
Нет специальной функции для явного ограничения аргументов шаблона (*), но вы можете использовать (С++ 11)
static_assert( std::is_base_of<GenericHost, hostType>::value,
"The template argument must be derived from GenericHost" );
(*) На С++ 17 будут ограничены шаблоны.
Это утверждение времени компиляции и может использоваться как объявление:
template<class hostType>
class GenericNetwork {
static_assert( std::is_base_of<GenericHost, hostType>::value,
"The template argument must be derived from GenericHost" );
public:
enum NetworkType{
central,
peripheral
}
virtual ~GenericNetwork(); // you typically want a virtual dtor in an ABC
hostType& getMasterHost(){ // you might want to return a (const) reference
return masterHost;
}
virtual std::string getName() = 0; // abstract
private:
NetworkType type_;
hostType masterHost; // or protected
// consider making the copy ctor and copy assignment op protected
// to prevent unintended slicing
}