Home > Back-end >  How to properly pass objects across multiple classes in C
How to properly pass objects across multiple classes in C

Time:06-28

I am mostly self thought with C and I have built some large complex codes during my PhD research. One of the issues I have been continuously facing is lack of oversight during development as I am nearly the only one who can code in the entire group. So when I make a "bad" code, there's no one to correct me and make me kick off a bad habit.

Question I have regards the following scenario:

I need to solve several iterative systems of equations for Physics problem I am studying. The package I built has around 30 classes and when I reviewed my code I noticed that I often unnecessarily copy objects. Consider this example:

class A
{
 std::valarray<double> _someArray;
 public:
    const double doSomethingComplicated() const;
    A(const std::valarray<double> &someArray) : _someArray(someArray) {...};
}
class B
{
 A _objectA;
 public:
    const double doSomethingComplicatedWithObjectA() const;
    B(const A &objectA) : _objectA(objectA) {...};
}
class C
{
 A _objectA;
 B _objectB;
 public:
    const double doSomethingComplicatedWithObjectAandB() const;
    C(const A &objectA, const B &objectB) : _objectA(objectA), _objectB(objectB) {...};
}

Disregard constructor definition in class declaration and what ... code in constructor is. The point is, in more complex classes as B and C, I am making private fields of previously made objects and in their constructors I am making copies of them. A lot of classes in my project do not change, so first time I make class A object, that will be it till the of code. I feel by making class A fields in more complex objects I am unnecessarily creating copies of A, even if it is just a "shallow" copy. I feel I can make much better code if I did this:

class B
{
public:
        const double doSomethingComplicatedWithObjectA(const A &objectA) const;
        B() {...};
};
class C
{
public:
        const double doSomethingComplicatedWithObjectAandB(const A &objectA, const B &objectB) const;
        C() {...};
};

In this case I would pass references of objects A and B to methods of more complex classes when something is needed. So there won't be an unnecessary copy and the same chunk of memory would be used. Obvious issue is that I may have 10 methods that need object A, and every time method needs object B, I also need to pass object A in those methods as B depends on A. So I may end up with very messy code, and feel that's not the way to go.

What would be a good recommendation to handle code like this?

My gut feeling is to use pointers, for instance in class C, I could have A* and B* fields, but I am then unsure how to write constructors properly and if I need to do some destructor management. I clearly don't have much experience with pointer fields, and my fields are either stuff from STL or other libraries, so I rarely need to implement destructors. If C has A* field, how do I make sure its destructor does not delete A object. I only want A* pointer to point to A object wherever it is in memory when first created by some other code, and that code that creates it, will automatically destroy it, not class C, class C just needs to stop pointing at that chunk of memory when C is destroyed.

Another feeling I have is to use shared or other types of smart pointers, but I understand those even less. I think the problem is my understanding of pointers and my lack of experience with them.

In essence my issue is in understanding C as I have no one to ask, and literature feels overwhelming, which is why I am here.

CodePudding user response:

If I understand your concern correctly, you just want to make class C have pointers to A and B, which are the instances that the instance of class C is going to work on.

I am going to explain this on example. Consider the following code

#include <iostream>
class C
{
    int* _num;
public:
    C(int* num) : num(_num)
    {}
    // there may be some methods
    // ...
    // ...
    ~C()
    {
        std::cout << "C destroyed" << std::endl;
    }
};

int main()
{
    int* my_int = new int(5); // dynamically allocating int variable
    {
     // I am going to put C in another scope so that at the end it will be destroyed.
    C c(my_int);
    }
    std::cout << *my_int;
    delete my_int;
    return 0;
}

If you execute the code you will see the following output.

C destroyed
5

Which means after the destruction of instance we can still access the memory by my_int pointer, hence the class did not deallocate the memory pointed by _num. Moreover, there is widespread practice of class having pointer to the instance it is going to work on, but deallocating the instance may not be its responsibility. In this case the destructor of C does not deallocate the memory pointed by my_int. If you want to know more about ownership and automated management of memory, I would advise to look up RAII. The smart pointers are implemented using RAII idiom. The first I would advice to look up is std::shared_ptr.

CodePudding user response:

Hopefully I understood your issue:

Did you try setting the private members to pointer type? (in regard to which subclass you wish it to point to)

That way, you'll pass pointer addresses as parameters.

Also, note that when you receive an object by reference as a method parameter, a copy of that object is not created, you can look at it as a "direct access" to the object, rather than a copy (which will take extra memory).

I hope I managed to assist, had a little hard time fully comprehending your question.

  •  Tags:  
  • c
  • Related