HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Is usage of friend class appropriate in this case?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thiscaseappropriateusagefriendclass

Problem

I want to have a class that stores data from an input file, for use in a simulation program. It is like a struct but may require some small functions for accessors.

A seperate class reads all the data from a text input file. I want to store most of this data in the above mentioned object. Therefore this class needs to be able to set the data variables.

My current approach is to give the loader class friend access, so that these variables can be set. No other classes should be able to set the data, therefore I am not using setters.

Is this appropriate friend class usage?

I have included a demonstration example of the situation, as simple as possible:

In reality the data holder I have called "Pod" here, holds 10 to 20 different strongly related variables.

#include 
class Pod {
 // holds the data that will be used later in simulation
 friend class Loader;
 public:
    double get_angle() {return angle_ * 3 / 180;};
 protected:
    double angle_;
};

class Loader {
 // loads data from text file.
 public:
    bool CheckFile() {
        // checks file:
        return true;
    }
    Pod ReadData() {
        double a = 90; // this line simulates reading from file.
        Pod pod;
        pod.angle_ = a;
        return pod;
    }
};

void DoesThingsWithData(Pod data) {
    std::cout << data.get_angle() << std::endl;
}

int main() {
    Pod my_data;
    {
        Loader loader;

        if (loader.CheckFile()) {
            my_data = loader.ReadData();
        }
    }
    // do things with data
    DoesThingsWithData(my_data);
    return 0;
}

Solution

Personally, I don't have major objections with that design. It gets the job done in a fairly straightforward way. But it does creates coupling, which might become a nuisance to you as the project grows. So another option with less coupling, as mentioned in comments, would be defining a default parameterized constructor:

class Object {
public:
    Object();
private:
    
};

class Loader {
public:
    Object LoadObject()
    {
        // many fields of data...
        int a    = ...
        double b = ...
        std::vector c = ...

        return Object(a, b, std::move(c), ...);
    }
};


In C++11, you also don't have to worry about unnecessary copies of complex objects such as a std::vector. You can make the constructor take a move ref, e.g.: std::vector && vec and apply std::move in the call site.

Code Snippets

class Object {
public:
    Object(<several params>);
private:
    <the data>
};

class Loader {
public:
    Object LoadObject()
    {
        // many fields of data...
        int a    = ...
        double b = ...
        std::vector<int> c = ...

        return Object(a, b, std::move(c), ...);
    }
};

Context

StackExchange Code Review Q#68485, answer score: 4

Revisions (0)

No revisions yet.