使用智能指针将右值引用绑定到抽象类的替代方法



>UPDATE

事实证明,STL的某些部分实际上可以使用 - 但由于严重的内存限制,要小心。Arduino对我来说是一个新平台,在看到很多帖子谈论缺乏对STL甚至某些端口的支持后,我只是认为它不能被使用。

现在我知道我可以使用unique_ptr,我原来的问题不再有任何意义了。无论如何,我都会把它留在这里,以防万一它对某人有用。

------

我正在Arduino项目中创建一个C++类 - 这意味着STL不可用(例如,没有智能指针)。目前我有以下内容:

class ntp_client {
public:
ntp_client(UDP& udp) : udp_(udp) {}
// ...
private:
UDP& udp_;
};

UDP是其他类(如WiFiUDPEthernetUDP)的基本抽象类。现在我的代码如下所示:

WiFiUDP udp;
ntp_client ntp(udp);

但是,我也希望能够执行以下操作:

ntp_client ntp{WiFiUDP{}};

所以,我想添加一个采用右值引用的构造函数,但我认为没有任何方法可以将右值引用绑定到类属性(因为 UDP 是一个抽象类)。

是否可以在不使用智能指针或模板的情况下执行此操作?

不,没有智能指针或模板就不可能做你正在做的事情。执行ntp_client ntp{WiFiUDP{}};后,WiFiUDP对象将超出范围并被破坏。你不能用对udp的右值引用来捕获它,因为你不能为它分配空间,无论是作为成员还是在堆上。

如果你真的想做这样的事情,你要么需要让ntp_client知道要分配多少内存(通过模板),要么将*UDP分配到其他地方并在ntp_client中存储指向它的指针。如果你只需要在ntp_client中使用udp对象,那么你可以添加一个构造函数,一个指向udp的指针,你可以将其作为ntp_client ntp{new WiFiUDP()};调用,并在ntp_client析构函数中添加一个delete。或者,您可以实现一个简单的非模板化udpsmartpointer类,该类围绕udp指针实现引用计数,并为您执行newdelete

总的来说,我认为最好的解决方案是你代码中已有的解决方案!

我会重新审视你的设计。我会这样做如下:

class ntp_client {
public:
template<class UDP_IMPL, ARGS...>
ntp_client(ARGS&&... args) : udp_(my::make_unique<UDP_IMPL>(std::forward(ARGS)args...))
{ }
private:
my::unique_ptr<UDP> udp_ptr;
};

实施my::unique_ptrmy::make_unique被留作一项练习,因为它是微不足道的。

解释 - 不要保留对你并不真正想拥有的东西的引用。相反,构建正确的实现并保留它。

如果你想用r值引用(即临时的)构造ntp_client,你需要给临时的住处。

逻辑位置将在ntp_client内,或者从中派生出某种东西。

这开始争论一种工厂函数,该函数要么使用封装的wifi或以太网UDP制作ntp_client,要么引用一个。

是否可以在不使用智能指针或模板的情况下执行此操作?

是的,但是模板使它更容易,打字更少...

#include <utility>
struct UDP {};
struct WiFiUDP : UDP {};
struct EthernetUDP : UDP {};

class ntp_client {
public:
ntp_client(UDP& udp) : udp_(udp) {}
// ...
private:
UDP& udp_;
};
class Wifi_ntp_client : public ntp_client
{
// take care - the base class reference is initialised before the stored
// object. You must not use it in the base class constructor
// or destructor!
Wifi_ntp_client(WiFiUDP&& w)
: ntp_client(_store)
, _store(std::move(w))
{}
WiFiUDP _store;
};
class Ethernet_ntp_client : public ntp_client
{
// take care - the base class reference is initialised before the stored
// object. You must not use it in the base class constructor
// or destructor!
Ethernet_ntp_client(EthernetUDP&& e)
: ntp_client(_store)
, _store(std::move(e))
{}
EthernetUDP _store;
};
int main()
{
WiFiUDP a;
ntp_client aa(a);
EthernetUDP b;
ntp_client bb(b);
Wifi_ntp_client c(WiFiUDP());
Ethernet_ntp_client d(EthernetUDP());
}

如果使接口的一部分UDP具有创建副本(实际派生类)的克隆方法并返回指向新对象的指针,则可以使构造函数采用 r 值引用,克隆它,并将其存储在UDP *中,并确保在析构函数中调用 delete。

ntp_client(UDP& udp) : udp_(udp.clone()) {}
UDP * udp_ = nullptr;
~ntp_client(){delete udp_;}

最新更新