【问题标题】:Overriding the default constructor/destructor without implementing them覆盖默认构造函数/析构函数而不实现它们
【发布时间】:2013-10-27 11:22:23
【问题描述】:

我有以下课程:

//myClass.h

myClass{
    int data;
public:
    myClass();
    void foo1(int);
    void foo2();
    ~myClass();
};

//myClass.cpp

#include"myClass.h"
myClass::foo1(int a){
    data = a ;
}

// main
int main(){
    myClass m;
    m.foo1(10);
}

为什么我们可以有一个未实现的函数(即头文件中的函数原型,但.cpp文件中没有定义),但不能有一个未实现的默认构造函数或析构函数?

如果我未实现默认构造函数/析构函数(如上例所示),为什么会出现编译器错误?

当我没有实现构造函数时,我看到一个undefined reference to myClass::myClass(),当我没有实现析构函数时,我看到一个undefined reference to vtable for myClass。但是,如果我实现了这两个(即使是空块{}),并且未实现方法(例如foo2()),编译器不会抱怨。

构造函数/析构函数本质上不只是类中的方法吗?如果是这样,为什么我可以有一个未定义的方法foo2(),但没有未定义的构造函数或析构函数?

如果有人可以帮助我理解这一点(而不仅仅是“因为它就是这样”:),那将不胜感激。

【问题讨论】:

    标签: c++ constructor destructor


    【解决方案1】:

    如果是这样,为什么我可以有一个未定义的方法foo2(),但没有未定义的构造函数或析构函数?

    因为您使用的是构造函数和析构函数,而不是使用foo2()。添加一个调用它,你会得到一个链接时错误。

    【讨论】:

    • 我做到了,你是对的,我看到了“未定义的引用”错误。所以这意味着即使我在构造函数/析构函数/函数实现中没有代码,例如。 {} ,没关系,但保留它作为原型不是吗?为什么这样?两者本质上不一样吗?
    • @srihari:如果这里的答案有帮助,请考虑投票。此外,如果此处的答案回答了您的问题,请考虑通过单击答案旁边的绿色复选标记来接受它。
    • @JohnDibling:试过了。显然在我能做到这一点之前有几分钟的等待时间。
    • @srihari:啊,好的。没有纠缠的意思。只是有时新的海报并不完全了解这里的工作方式。你显然明白了。
    • @JohnDibling:大声笑,我的两位数名声暴露了我:P
    【解决方案2】:

    为什么我们可以有一个未实现的函数(即函数 标题中的原型,但 .cpp 文件中没有定义),但不是 未实现的默认构造函数或析构函数?

    你的断言是错误的。你可以有一个未实现的默认构造函数。事实上,这是确保您的类永远不会被隐式构造的一种方法。

    如果我未实现默认构造函数/析构函数(如 上面的例子),为什么会出现编译错误?

    因为您在代码的某处使用它。重新阅读您的编译器错误。它可能会告诉您确切的位置。

    这里是:

    int main(){
    myClass m;
    m.foo1(10);
    

    myClass m; 行实例化myClass,使用默认构造函数。

    如果你不使用默认构造函数,你可以声明但不实现默认构造函数并编译干净。考虑:

    class Foo 
    {
    public:
        Foo (int x) {}
        Foo();
    };
    
    int main()
    {
        Foo f (1);
    }
    

    这里有一个Foo() 的声明,但没有实现。代码编译时没有编译器或链接器错误。但是,这段代码不会编译干净:

    int main()
    {
        Foo f;
    }
    

    后一个例子是你想要做的,在这里:

    myClass m;
    

    【讨论】:

    • 未实现的默认构造函数 you 是什么意思?我的意思是只有一个原型,没有定义(即,甚至没有一个空代码块。)。我从上面的答案中了解到,未实现的构造函数会引发链接器错误,但正如您所说,空的默认构造函数可能很有用(例如,在单例类的某些设计中)。希望我们在同一页上 - 如果不是,请让我知道我误解了什么。谢谢。
    • @srihari:我的意思正是你的意思。如果您尝试使用未实现的默认构造函数,则只会生成链接器错误。如果你不尝试使用默认构造函数,你将编译干净。
    • 如果你声明了一个默认构造函数myClass(),但没有内联实现它(意味着你在类声明中将它声明为myClass() {})或实现文件(意味着你将有一个@ 987654330@ 某处),然后尝试实例化myClass 的对象(例如myClass a),链接器找不到默认构造函数(不带参数的构造函数),所以它会给你一个错误。跨度>
    • @John Dibling:我明白了。所以只要我从不创建这个类的对象,我就不会看到任何问题(编译器或链接器)。就像你说的,这个问题只发生在myClass m。明白了。
    • @ZacHowland:没错,但这并不是我真正理解的问题。我理解这个问题是:“为什么你可以有未实现的方法但没有未实现的默认构造函数?”答案是“你可以”。碰巧OP的示例代码无法编译,因为他试图使用未实现的默认构造函数。
    【解决方案3】:

    编译器只关心你声明的代码(这是你的类声明所做的)。如果你声明你的构造函数,它假定你已经在某个地方实现了它。链接器将在您使用它时尝试找到该实现。对于构造函数/析构函数,您在尝试创建类的实例时使用它。如果它们不存在,您将收到链接器错误。如果你不使用foo2(),并且你没有实现,链接器不需要找到它,所以它不会尝试。因此,您不会收到链接器错误,您的代码将被成功编译并链接到可执行文件中。

    【讨论】:

    • 两点:首先,如果你不提供一个实现,正式地,它是未定义的行为(虽然在实践中,我不知道你不会从中得到错误的实现链接器)。其次,如果没有析构函数,可以创建类的实例:new MyClass。 (但你仍然需要构造函数。)
    • @JamesKanze 如果您声明了一个析构函数但没有实现它,当您尝试在堆上销毁MyClass 的实例时,您将收到链接器错误。它不是真正的“未定义行为”,因为它不是直接处理语言,而是编译过程(如果你想获得技术,这是完全未定义的行为,因为它根本不是标准解决的问题) .
    • @JamesKanze:首先,您的意思是说“如果您不提供实现但尝试使用它 [...]”?或者您是说未实现(但已声明)的默认构造函数会导致 UB 即使您不使用它
    • @ZacHowland 如果你调用delete p,并且p 的类型是MyClass*,如果你没有定义析构函数,你会得到一个错误。但是如果你打电话给new MyClass,你就不会。只有当您尝试删除它时,您才会遇到问题;如果你从不删除它,那么你不需要定义析构函数。
    • @ZacHowland 关于未定义的行为:“每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的一个定义;不需要诊断。” (§3.2/3)我不知道任何编译器在缺少定义时不会给出错误,但根据标准,这是未定义的行为。
    【解决方案4】:

    为什么我们可以有一个未实现的函数(即头文件中有函数原型,而.cpp文件中没有定义),却不能有一个未实现的默认构造函数或析构函数?

    必须定义任何使用的函数。如果不使用,大多数函数可以保持未定义。

    如果我未实现默认构造函数/析构函数(如上面的示例),为什么会出现编译器错误?

    因为你的程序创建和销毁你的类的一个实例,它使用构造函数和析构函数;因为它们被使用,所以它们必须被定义。你已经声明了它们,所以它们不会被隐式定义;因此,您必须自己定义它们。

    当我没有实现构造函数时,我看到了对 myClass::myClass() 的未定义引用;

    那是因为构造函数是用来创建对象的。

    当我没有实现析构函数时,对 myClass 的 vtable 的未定义引用。

    这是因为此编译器在与析构函数相同的翻译单元中生成 vtable(如果它既不是隐式的也不是内联的)。如果你不声明析构函数但不定义它,你也不会得到vtable

    但是,如果我实现了这两个(即使使用空块 {}),并且未实现方法(例如 foo2()),编译器不会抱怨。

    那是因为您不使用该功能。如果你要调用它,那么你会得到一个错误。

    构造函数/析构函数本质上不只是类中的方法吗?

    或多或少,是的;但它们在各个方面都很特别。语言规范将它们描述为“特殊成员函数”,并有一整章描述它们的属性。

    如果是这样,为什么我可以有一个未定义的方法foo2(),但没有未定义的构造函数或析构函数?

    重申:你必须定义你使用的任何东西。如果实例化类,则使用构造函数和析构函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-21
      • 2017-11-10
      • 1970-01-01
      • 2012-12-24
      • 2014-01-09
      • 1970-01-01
      • 2016-03-25
      • 1970-01-01
      相关资源
      最近更新 更多