>UPDATE
事实证明,STL的某些部分实际上可以使用 - 但由于严重的内存限制,要小心。Arduino对我来说是一个新平台,在看到很多帖子谈论缺乏对STL甚至某些端口的支持后,我只是认为它不能被使用。
现在我知道我可以使用unique_ptr
,我原来的问题不再有任何意义了。无论如何,我都会把它留在这里,以防万一它对某人有用。
------
我正在Arduino项目中创建一个C++类 - 这意味着STL不可用(例如,没有智能指针)。目前我有以下内容:
class ntp_client {
public:
ntp_client(UDP& udp) : udp_(udp) {}
// ...
private:
UDP& udp_;
};
UDP
是其他类(如WiFiUDP
或EthernetUDP
)的基本抽象类。现在我的代码如下所示:
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
指针实现引用计数,并为您执行new
和delete
。
总的来说,我认为最好的解决方案是你代码中已有的解决方案!
我会重新审视你的设计。我会这样做如下:
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_ptr
和my::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_;}