首页 > 解决方案 > 如何解决对我自己的类的构造函数和析构函数的未定义引用的问题?

问题描述

我写了一个简单的程序,它使用一个带有构造函数、析构函数和数据成员的类。当我尝试初始化一个对象时,调试器告诉我没有析构函数和析构函数。我在Clion,VS2019和VSCode中尝试过。我得到了与 4) 相同的结果。我不知道它在我实际创建构造函数和析构函数时一直发生。

请帮助我。谢谢!

1) 字符串.h

#ifndef TEST_STRING_H
#define TEST_STRING_H

class String {
  public:
    explicit String(const char *cstr = nullptr);

    String(const String &str);

    String &operator=(const String &str);

    ~String();

    char *get_c_str() const { return m_data; }

  private:
    char *m_data;
};

#endif // TEST_STRING_H

2) 字符串.cpp

#include "String.h"
#include <cstring>

inline String::String(const char *cstr) {
    if (cstr) {
        m_data = new char[strlen(cstr) + 1];
        strcpy(m_data, cstr);
    } else {
        m_data = new char[1];
        *m_data = '\0';
    }
}

inline String::~String() { delete[] m_data; }

inline String &String::operator=(const String &str) {
    if (this == &str)
        return *this;

    delete[] m_data;
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
    return *this;
}

inline String::String(const String &str) {
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
}

3) main.cpp

#include <iostream>
#include "String.h"

using namespace std;

int main() {
    String s1("hello");
    return 0;
}

4) 结果

/tmp/ccCIK0hs.o: In function `main':
/home/Projects/test/main.cpp:7: undefined reference to `String::String(char const*)'
/home/Projects/test/main.cpp:7: undefined reference to `String::~String()'
collect2: error: ld returned 1 exit status

标签: c++linux

解决方案


您使用说明符的方式存在“问题” inline。修复只是inline从所有String方法定义中删除说明符以使错误消失。

让我们深入研究为什么您的程序格式错误。

我将使用cppreference 内联说明符中的点:

内联函数或内联变量 (C++17 起) 具有以下属性:

  1. 内联函数或变量的定义(C++17 起)必须在访问它的翻译单元中是可访问的(不一定在访问点之前)。
  2. 具有外部链接(例如未声明为静态)的内联函数或变量(C++17 起)具有以下附加属性:
    1. 程序中可能有多个内联函数或变量的定义(C++17 起),只要每个定义出现在不同的翻译单元中,并且(对于非静态内联函数和变量(C++17 起) )) 所有定义都是相同的。例如,内联函数或内联变量 (C++17 起) 可以在多个源文件中 #include 的头文件中定义。
    2. 它必须在每个翻译单元中声明为内联。
    3. 它在每个翻译单元中具有相同的地址。

广告 1)简单来说:如果你有一个像inline String::String(const char *cstr) { something }你这样的内联函数定义,你可以从同一个.cpp文件中调用它。如果您从其他.cpp文件中调用它,例如您在 in 中定义了函数String.cpp但从 中调用它main.cpp,那是无效的。

广告 2)您的函数不是静态的,因此本节适用。

广告 2.1)如果你有inline String::String(const char *cstr)你的String.cpp,你必须有inline String::String(const char *cstr);在类声明中String.h。如果你用 声明它inline,它也必须inline无处不在String.h。现在在您的代码中,String.h它没有inline.

解决方法是遵守点数规则12.1. 因此,要么删除inline关键字String.cpp,让所有函数都成为具有外部链接的普通函数,这是正常的编程方式。

或者,如果您必须让您的函数inline将所有代码String.cpp移至这些成员函数声明String.h并添加inline到这些成员函数声明中。像下面的代码:

#ifndef TEST_STRING_H
#define TEST_STRING_H

class String {
  public:
    inline explicit String(const char *cstr = nullptr);
    inline String(const String &str);
    inline String &operator=(const String &str);
    inline ~String();
    inline char *get_c_str() const { return m_data; }
  private:
    char *m_data;
};

#include <cstring>

inline String::String(const char *cstr) {
    if (cstr) {
        m_data = new char[strlen(cstr) + 1];
        strcpy(m_data, cstr);
    } else {
        m_data = new char[1];
        *m_data = '\0';
    }
}

inline String::~String() { delete[] m_data; }

inline String &String::operator=(const String &str) {
    if (this == &str)
        return *this;

    delete[] m_data;
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
    return *this;
}

inline String::String(const String &str) {
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
}

#endif // TEST_STRING_H

请注意,在“内部”类定义中定义的函数是隐式的inline,因此您也可以在类定义中移动函数定义。


推荐阅读