-2

I wrote a simple program that uses a class with a constructor, a destructor and a data member. When I tried to initialize an object, the debugger told me that there was not a destuctor and a destructor.I tried it in Clion, VS2019 and VSCode. And I got the same result like 4). I don't know it occurs all the time when I have actually created the constructor and destructor.

Pls help me. THANKS!

1) String.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) String.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) The result

/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
Cheny Liu
  • 3
  • 2

1 Answers1

0

There are "problems" with the way you used inline specifier. The fix is simply to remove the inline specifier from all String method definitions to make the error go away.

Let's delve into why your program is ill-formed.

I'll be using the points from cppreference inline specifier:

An inline function or inline variable (since C++17) has the following properties:

  1. The definition of an inline function or variable (since C++17) must be reachable in the translation unit where it is accessed (not necessarily before the point of access).
  2. An inline function or variable (since C++17) with external linkage (e.g. not declared static) has the following additional properties:
    1. There may be more than one definition of an inline function or variable (since C++17) in the program as long as each definition appears in a different translation unit and (for non-static inline functions and variables (since C++17)) all definitions are identical. For example, an inline function or an inline variable (since C++17) may be defined in a header file that is #include'd in multiple source files.
    2. It must be declared inline in every translation unit.
    3. It has the same address in every translation unit.

Ad 1) In simple words: If you have an inline function definition like inline String::String(const char *cstr) { something } you are allowed to call it from the same .cpp file. If you call it from other .cpp file, like you defined function in String.cpp but called it from main.cpp, that's invalid.

Ad 2) Your functions are not static, so this section applies.

Ad 2.1) If you have inline String::String(const char *cstr) in your String.cpp, you have to have inline String::String(const char *cstr); in class declaration in String.h. If you declare it with inline, it has to be inline everywhere, in String.h too. Right now in your code, in String.h it does not have inline.

The fix is to adhere to rule in points 1 and 2.1. So either remove the inline keyword from String.cpp and have all your functions just be normal functions with external linkage, which is the normal way of programming.

Alternatively, if you have to have your functions with inline move all code from String.cpp to String.h and add inline to those member function declarations. Like the following code:

#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

Note that functions defined" inside" class definition are implicitly inline, so you could also move your function definition inside class definition.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111