2

我刚刚在一些遗留代码中偶然发现了这个错误:

class MyAPIHandler
{
    private:
     int handle;
    public:
    void MyApiHandler()  // default constructor
    {
        handle = 42;
    };
};

它编译得很好,没有警告 - 但行为不是我想要的,因为构造函数名称拼写错误。这本身会产生关于“函数不返回值”的警告,但我想我是在自动驾驶仪上并添加了一个“void”返回类型来“修复”这个问题。

现在,错误修复很容易,但我的问题是:-

我可以使用哪些技术来防止此类错误再次发生?

一些语言需要一个明确的“构造函数”关键字,这应该使这个问题显而易见。单元测试,显然也应该抓住它。我还可以做些什么?

4

13 回答 13

19

If you always use initialiser lists in your constructors:

MyApiHandler()  // default constructor
: handle(42)
{
}

the misnamed constructor bug would be even more unlikely, and it's better style anyway.

Edit: thanks to commenter for the link

于 2008-12-01T11:14:24.047 回答
2

代码审查?正如你提到的,单元测试也很好。代码覆盖率。许多用于查找错误的常用工具都可以找到这一点。

于 2008-12-01T10:51:53.293 回答
2

除了小心或进行代码审查之外,您无能为力

您可以制作一个清单,以编写具有以下主题的新课程:

  • 复制粘贴要用作第一个构造函数的类名。
  • 如果实现复制构造函数,则实现析构函数和复制赋值运算符(三规则
  • 注意单参数构造函数或具有默认参数的构造函数 - 考虑使它们显式

These are constructor-related points, the whole checklist could be longer. After a while you'll start doing things like this automatically.

Personally, I think Unit Testing is the best answer to avoid your specific problem, as you already mentioned.

EDIT: Added idea from comments:
In some development environments you could use code templates or macros to generate a correct class skeleton for you. This is a real "programmer's solution" - automating everything that can be automated to avoid errors.

于 2008-12-01T10:55:30.307 回答
2

This shouldn't be much of a problem. Constructors don't have return values (not even void), so the compiler should be able to tell whether something is meant to be a constructor or a member function, and let you know if there's a problem. Obviously someone saw the compiler's error on that and chose the wrong solution (adding a return type instead of correcting the spelling). (EDIT: Sorry, didn't realize that you'd done that yourself.)

Personally, I always put the constructor(s) near the beginning of the the class's public section (before any member functions, but after public type definitions and constants), so a problem like that would be pretty obvious to me on re-reading the code. But conventions differ on that.

于 2008-12-01T18:13:20.393 回答
1

I'm pretty sure that PC Lint, a static code analysis tool, would have spotted this error. It isn't free but it is very, very good. Worth a look:

http://www.gimpel.com/

于 2008-12-01T10:58:59.833 回答
1

I believe you should not think too much about this issue

It happens for constructors, but also for any other method, or even function. For example:

class MyBase
{
   // etc.
   virtual void doSomething() ;
} ;

class MyDerived : public MyBase
{
   // etc.
   virtual void DoSomething() ;
} ;

Or:

bool isOk(int value) { /* etc. */ }
bool isOK(double value) { /* etc. */ }

void doSomething(int value)
{
   if(isOK(value)) // isOK(double) will be called
   {
      // Etc.
   }
}

And this is not only a problem of character case. This kind of error happens. IDE helpers like autocompletion can help somewhat, as could a good unit-testing code (something covering all methods of a class), but I don't believe the compiler alone could protect against this kind of developer mistyping even with additionnal keywords.

What about CONSTRUCTOR?

As for the CONSTRUCTOR define mentionned before me, this is a bad idea IMHO:

It will help as much as a comment (and so, why not use /* CONSTRUCTOR */ instead?), and if someone thought about using the word CONSTRUCTOR as a define symbol, then you can bet someone else will have the same idea in some header you include but don't own, and that it will break your code...

于 2008-12-01T18:38:34.740 回答
1

If the member variable is not going to change, declare it const and the compiler will force you to use an initialiser list, which in this case would force you to detect your error (because it won't compile).

于 2009-03-17T05:46:13.353 回答
0

也许你可以做一个

#define CONSTRUCTOR

然后在你的代码中

class MyAPIHandler
{
   public:
       CONSTRUCTOR MyAPIHandler()
       {
           // Deep magic
       }
};

现在,它本身不会做任何事情,但如果你习惯这样写,你应该能够更容易地发现错误。

尽管老实说,我认为这种情况非常罕见,不值得麻烦。

于 2008-12-01T10:54:24.210 回答
0

I guess following TDD will help, though not the solution you were looking for ...

as you said its legacy code, i suggest you read "working with legacy code- Kent Beck". That might help.

regression testing will pick issues due to bug fixes.

even i am looking forward for out of box suggestions :)

于 2008-12-01T11:01:21.090 回答
0

Unit tests.

MyAPIHandler mah;
BOOST_CHECK_EQUAL(mah.handle, 42);
于 2008-12-01T13:56:55.120 回答
0

I have a set of broilerplates/templates (in Vim.. but I guess you can have them in any modern editor) which avoids typos of this nature.

于 2008-12-01T18:45:44.460 回答
0

Most IDEs allow for macros of some sort. Just use a macro to create a class definition. That's the way I have it setup. My macro prints outs:

class CHANGETHIS
{
    public: 
      CHANGETHIS();
      ~CHANGETHIS();
}
#error "Finish this class definition"

The #error line is just a safety net to make sure it doesn't build if I am interrupted in the middle of my work and forget about it when I get back.

What I like about this is that its portable across any editor, so I don't feel handicapped when I switch to a different IDE. Hope this helps.

In addition, use initialization lists. They're faster and lead to less bugs.

于 2008-12-01T20:56:12.857 回答
0

I'd say the simplest way of avoiding this problem is to lay down the law for naming in the form of a coding style guide. In this you set down how to name your entities and you stick to it.

Specifically at work we've got it mandatory that abbreviations are only capitalised on the first letter, therefore this would be caught more easily.

于 2009-01-02T16:09:38.143 回答