VansFannel
VansFannel

Reputation: 46011

Instance a variable declared in the header file without default constructor

I've just started to learn C++ using Visual Studio 2019.

I'm trying to move the variable declaration from a class method to the header file. Originally, this is my class:

The header file:

#pragma once
#include <string>
 
class MyClass
{
public:
    MyClass();

    ~MyClass();

    bool Connect(std::string host, std::string port);
};

And the Cpp file:

#include "MyClass.h"
#include "TelnetClient.h"

#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

MyClass::MyClass()
{
}

MyClass::~MyClass()
{
}

bool MyClass::Connect(std::string host, std::string port)
{
    // Omitted for brevety

    // Create a Telnet client
    TelnetClient telnet(io_service, iterator);

    // Omitted for brevety
}

But, this version doesn't compile (here, I have moved telnet declaration to the header file:

#pragma once
#include <string>

class TelnetClient;

class MyClass
{
public:
    MyClass();

    ~MyClass();

    bool Connect(std::string host, std::string port);

private:
    TelnetClient telnet;
};

And the Cpp file:

#include "MyClass.h"
#include "TelnetClient.h"

#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

MyClass::MyClass()
{
}

MyClass::~MyClass()
{
}

bool MyClass::Connect(std::string host, std::string port)
{
    // Omitted for brevety

    // Create a Telnet client
    telnet(io_service, iterator);

    // Omitted for brevety
}

With this new version, I get two errors.

One, in the new header file:

telnet uses class 'TelnetClient' undefined.

And another one, inside the Cpp file:

error C2064: the term is not evaluated as a function with 2 arguments

I have checked Constructors and member initializer lists, and they don't say how can I do it.

How can I instance the telnet variable declared in the header file?

UPDATE:

I have updated the header file, removing the forward declaration and adding the #include "TelnetClient.h" and, in the method, changed this:

telnet = TelnetClient(io_service, iterator);

And now I get two new error messages.

One, in the MyClass constructor:

error C2512 'TelnetClient': no suitable default constructor available

And the second one in the new telnet = TelnetClient(...:

error C2280 'TelnetClient & TelnetClient :: operator = (const TelnetClient &)': Attempting to reference a removed function.

Upvotes: 0

Views: 925

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 598414

In your .h file, the compiler doesn't know what the TelnetClient type looks like, as you are only forward declaring it, so you can't declare any instances of TelnetClient in your header, only TelnetClient* pointers and TelnetClient& references. You would need to move the #include "TelnetClient.h" statement from your .cpp file into your .h file, and get rid of the forward declaration.

Then, inside of your Connect() method, you are still trying to call the TelnetClient's constructor direct on the telnet member, which won't work in that context anymore. But, you can create a new instance and move it into the existing instance.

For example:

#pragma once
#include <string>
#include "TelnetClient.h"

class MyClass
{
public:
    MyClass();

    ~MyClass();

    bool Connect(std::string host, std::string port);

private:
    TelnetClient telnet;
};
#include "MyClass.h"

#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

MyClass::MyClass()
{
}

MyClass::~MyClass()
{
}

bool MyClass::Connect(std::string host, std::string port)
{
    // Omitted for brevety

    // Create a Telnet client
    telnet = TelnetClient(io_service, iterator);

    // Omitted for brevety
}

UPDATE: Actually, this won't work in your case, because this error:

error C2512 'TelnetClient': no suitable default constructor available

means TelnetClient does not have a parameter-less default constructor, so you can't construct a TelnetClient object without passing parameters into its constructor. Which means if you intend to declare a TelnetClient instance in your .h file then you must use your MyClass constructor's member initialization list to construct it with parameters. Also, because this error:

error C2280 'TelnetClient & TelnetClient :: operator = (const TelnetClient &)': Attempting to reference a removed function.

means that TelnetClient has disabled its copy assignment operator, and doesn't have a move assignment operator, so telnet = TelnetClient(io_service, iterator) will not work, either.

For example:

#pragma once
#include <string>
#include "TelnetClient.h"

class MyClass
{
public:
    MyClass();

    ~MyClass();

    bool Connect(std::string host, std::string port);

private:
    TelnetClient telnet;
};
#include "MyClass.h"

#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

MyClass::MyClass()
    : telnet(io_service, iterator) // <-- you will have to get these params from somewhere, or pass them in to MyClass() itself
{
}

MyClass::~MyClass()
{
}

bool MyClass::Connect(std::string host, std::string port)
{
    // Omitted for brevety
}

Otherwise, you will have to change the telnet member to be a TelnetClient* pointer instead, then you can continue using your forward declaration in the .h file, and your Connect() method can new a TelnetClient object using its 2-parameter constructor. But then you will have to update MyClass to follow the Rule of 3/5/0 to manage that pointer correctly to avoid leaks and such.

For example:

#pragma once
#include <string>

class TelnetClient;

class MyClass
{
public:
    MyClass();
    MyClass(const MyClass &) = delete;

    ~MyClass();

    MyClass& operator=(const MyClass &) = delete;

    bool Connect(std::string host, std::string port);

private:
    TelnetClient* telnet;
};
#include "MyClass.h"
#include "TelnetClient.h"

#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

MyClass::MyClass()
    : telnet(NULL)
{
}

MyClass::~MyClass()
{
    delete telnet;
}

bool MyClass::Connect(std::string host, std::string port)
{
    // Omitted for brevety

    // Create a Telnet client
    delete telnet;
    telnet = new TelnetClient(io_service, iterator);

    // Omitted for brevety
}

Upvotes: 1

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 123548

A forward declaration is not sufficient to declare a member variable. The definition of TelnetClient must be available. Remove the forward declaration and include "TelnetClient.h" in the header.

Members are initialized by the constructor not in arbitrary methods (actually before the constructor runs). When you want to assign a new instance to the member you need to use its operator= (provided it has one) to assign a new instance:

bool MyClass::Connect(std::string host, std::string port)
{
    // Omitted for brevety

    // Create a new Telnet client
    telnet = TelnetClient(io_service, iterator);

    // Omitted for brevety
}

However, if possible you should properly construct the member in the contructor already:

MyClass::MyClass(io_service_t io_service, iterator_t iterator) : telnet(io_service,iterator)
{
}

Upvotes: 1

Related Questions