我最近开始使用C++为我正在进行的项目尝试和编程Arduino库。在这个库中,我包含了另一个发布在GitHub上的库,我认为它可以自己编译。作为类构造函数的一部分,我正在创建并分配已发布类的对象。当我用两个整数调用构造函数时,我得到以下错误:
Cell.cpp:7:31: error: no match for call to '(DS3904) (int&, int)'
_digipot(digipotAddress, 2);
^
编辑:在编辑之前,评论告诉我,在这种情况下,引用应该很好。这是我正在使用的图书馆。以下是头文件:
Cell.h:
#ifndef Cell_h
#define Cell_h
#include "Arduino.h"
#include "DS3904.h"
class Cell {
public:
Cell(int cellNumber, int digipotAddress, int resistorAddress);
void setCellVoltage(int voltage);
int getCellNumber();
private:
unsigned int _cellNo;
unsigned int _resistorAddress;
DS3904 _digipot;
};
#endif
实际的C++文件Cell.cpp(注意,这个文件中省略了一些未使用的函数):
#include "Arduino.h"
#include "Cell.h"
Cell::Cell(int cellNumber, int digipotAddress, int resistorAddress) {
_digipot(digipotAddress, 2);
_cellNo = cellNumber;
_resistorAddress = resistorAddress;
}
这实际上与指针或引用无关。您在调用成员对象_digipot
的构造函数时使用了错误的语法,并且您的代码被解析为不同的东西,从而导致令人困惑的错误消息。
您有一个类Cell
,它有一个类型为class DS3904
的成员_digipot
。因此,Cell
的构造函数需要构造_digipot
,但在C++中,这不是通过在Cell
构造函数的主体中调用_digipot
的构造函数来实现的。相反,它需要在成员初始值设定项列表中完成。因此,编写构造函数Cell::Cell
的正确方法是:
Cell::Cell(int cellNumber, int digipotAddress, int resistorAddress)
: _digipot(digipotAddress, 2),
_cellNo(cellNumber),
_resistorAddress(resistorAddress)
{
// no code needed in the body
}
这个想法是,在C++中,程序中永远不应该有任何一点可以看到一个存在但尚未完全构造的对象,唯一的例外是该对象自己的构造函数。如果由Cell::Cell
的主体代码来调用_digipot
的构造函数,那么它也可以在调用构造函数之前尝试访问_digipot
,此时它会发现它处于未构造状态。因此,必须在Cell::Cell
的主体开始执行之前构造_digipot
,但仍然需要有一种方法来指定应该调用_digipot
的哪个构造函数,以及使用什么参数。成员初始值设定项列表就是为了实现这一点而发明的。
(从技术上讲,你不必在成员初始值设定项列表中包括int
成员_cellNo
和_resistorAddress
:如果你忽略了它们,它们会被初始化为不确定的值,然后你可以像现有代码一样在构造函数的主体中为它们赋值。但在成员初始项列表中做所有事情会更干净。)
那么奇怪的错误消息是从哪里来的?实际上,您的代码有两条错误消息,其中您只发布了第二条,第一条是信息更丰富的一条:
Cell-old.cpp: In constructor ‘Cell::Cell(int, int, int)’:
Cell-old.cpp:4:67: error: no matching function for call to ‘DS3904::DS3904()’
4 | Cell::Cell(int cellNumber, int digipotAddress, int resistorAddress) {
|
这是第一个问题。您没有指定成员初始值设定项列表,但仍然需要构造_digipot
,因此默认情况下调用对象的默认构造函数,即不带参数的构造函数。但是class DS3904
没有这样的构造函数,所以这是一个错误。
第二个问题是,构造函数主体中的语句_digipot(digipotAddress, 2);
没有被解析为试图调用_digipot
的构造函数(毕竟,这应该是不可能的,因为当我们到达这里时,它应该已经被构造好了)。相反,形式为obj(arg, arg)
的语句将函数调用运算符operator()
应用于obj
。如果obj
是一个函数或函数指针,这将简单地调用该函数,但对于其他类型,该运算符也可以重载;请参阅为什么重写运算符()?这在哪些方面是有用的。
因此,编译器会寻找一个重载的DS3904::operator()
,它采用与您传递的参数兼容的参数:一个int
左值,它可以作为引用(int&
)传递,另一个int
右值,它必须按值传递。不存在这样的过载,因此这将导致您的第二个错误。
顺便说一句,clang在这段代码上的错误信息更清晰了:
Cell-old.cpp:4:7: error: constructor for 'Cell' must explicitly initialize the member '_digipot' which does not have a default constructor
Cell::Cell(int cellNumber, int digipotAddress, int resistorAddress) {
^
./Cell.h:15:16: note: member is declared here
DS3904 _digipot;
^
./DS3904.h:27:7: note: 'DS3904' declared here
class DS3904
^
Cell-old.cpp:5:5: error: type 'DS3904' does not provide a call operator
_digipot(digipotAddress, 2);
^~~~~~~~
当遇到令人困惑的错误消息时,尝试不同的编译器,看看它是否能给你带来更有用的东西,这通常是很有帮助的。