0

For a scientific code I need to construct classes that require (member) pointers to member variables of other classes (i.e. both parents and siblings).

I have researched this for hours and found several topics about pointers to member variables, but nothing about my specific problem. Hence my question: what is the correct syntax to instantiate class C in the example below such that the pointer in class C points to the VectorXd beta in class B?

Edit: To clarify my question, the intended behavior is that a call to the class B member function PrintBetas() prints out the current value of VectorXd beta nr_C times.

#include <iostream>
#include <vector>
#include <Eigen/Dense>

using namespace Eigen;
using namespace std;


class C {
    
  public:    
    C( VectorXd *beta_IN ): beta( beta_IN ) {}
         
    void PrintBeta() {
      cout << "(*beta) = " << (*beta).transpose() << endl;  // ???
    }    

  private:
    const VectorXd *beta;
};


class B {
      
  public:            
    B( VectorXd beta_IN, int nr_C ): beta( beta_IN ) {
      for( auto i=0; i<nr_C; i++)
        vec.push_back( C( &beta ) );
    }
    
    void PrintBetas() {
      for (auto c : vec)
        c.PrintBeta();
    }

    VectorXd beta;
            
  private:    
      
   vector<C> vec;
          
};


class A {
  
  public: 
    A( int nr_B, int nr_C ) {
          
      for (auto i=0; i<nr_B; i++) {
        VectorXd beta = VectorXd::Random(3);
        subclass.push_back( B( beta, nr_C) );
      }
   }
   
   vector<B> subclass;
};
  
  

int main() {
    
  A test(2,3);
  test.subclass[0].PrintBetas();
  test.subclass[1].beta.setZero();
  test.subclass[1].PrintBetas();
  
}
Marzz
  • 49
  • 6
  • The correct syntax depends on what you are trying to do. I'd rather not try to infer your intent from flawed code. (If your code was not flawed, you would not be asking this question.) Please describe what you are trying to accomplish. Ideally, your description would be abstract enough that mentioning your class hierarchy becomes merely a bit of context tacked on just before the example code. *Hint: the description might start "I want `beta` to point to ...".* – JaMiT Oct 20 '20 at 00:09
  • Are you sure about: `that require (member) pointers`. That seems an odd use case. A more standard technique would be to use `std::function` then it can be any type of callable not a specific type. – Martin York Oct 20 '20 at 00:28
  • @JaMiT: Good point. In my example I want the beta pointer in class C to point to the beta vector in class B, so the class C member function can print the beta values to screen. – Marzz Oct 20 '20 at 00:40
  • @Marzz Is there a reason the pointer in `C` *has* to point to a member of `B`? I think you are setting yourself up to use a [pointer to class data member](https://stackoverflow.com/questions/670734) (see also [cppreference.com](https://en.cppreference.com/w/cpp/language/pointer#Pointers_to_members)), but why not simply use a pointer, as in `VectorXd*`? – JaMiT Oct 20 '20 at 23:46
  • @JaMiT: I studied that post and the example about the intrusive lists came close to what I was looking for. Alas, I still couldn't get the pointer in C to point to the member in B, hence this question. Please let me know if you know how this should be done in C++! As for the reason for member pointers, it is for my calculations the most elegant design of the C classes. And even if there was another way, I really would like to know how to initialize a class with member pointers to another class' member variables :) – Marzz Oct 21 '20 at 20:18
  • @Marzz *"I still couldn't get the pointer in C to point to the member in B, hence this question."* -- unfortunately, you chose an inconsistent approach. Your attempt to do this failed, but you did not ask what you did wrong. Instead, you made an assumption and asked an [XY question](https://en.wikipedia.org/wiki/XY_problem). A better question would start with a summary of your problem (describe the symptoms), followed by a description of what your code is supposed to do, then your example code. Perhaps speculate about your syntax being wrong after presenting the real problem. – JaMiT Oct 21 '20 at 23:49
  • Your setup seems wonky. Why does `C` need to store a pointer to a `VectorXd` instead of receiving a `VectorXd` as a parameter to `PrintBeta()`? (The reason might have been simplified away for your example.) I would work on this part of the design rather than (or at least before) trying to get your current design to work. See if you can get your desired functionality without the need for a pointer in `C`. – JaMiT Oct 21 '20 at 23:59

1 Answers1

0

In your code, whenever you copy a B object, the Cs inside the new B still point to the beta member in the old B. This is the problem you'd want to solve.

Option 1: Use only B pointers, wrapped in smart pointers like or shared_ptr. Now when you copy it does not copy the underlying B object, and all Cs remain intact.

Option 2: When you copy a B, overload the copy constructor to adjust all underlying Cs to point to the new object's beta member:

B::B(const B& other)
  : beta(other.beta),
    vec(other.vec)
{
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        it->beta = &beta;
    }
}

This may seem expensive, but you're copying all the Cs anyway, so it doesn't change the time complexity. However, you'd probably also want to overload the move constructor, and that's a different story:

B::B(B&& other)
  : beta(std::move(other.beta)),
    vec(std::move(other.vec))
{
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        it->beta = &beta;
    }
}

This now changes the time complexity from probably constant to probably linear.

Note: "Pointers to member variables" mean something different from pointers to variables which are inside objects, but neither would probably be of much use here. If you have a pointer to member, you might declare/define it like:

VectorXd B::* member_ptr = &B::beta;

and you would use it on a specific instance b of type B like:

VectorXd val = b.*member_ptr;

. If you define member_ptr the way I showed above, then this line just means:

VectorXd val = b.beta;

.

Anonymous1847
  • 2,568
  • 10
  • 16
  • The class B indeed does not properly initialize the pointer in class C. But in terms of solutions, how do you propose I change the class C constructor and the class C member function? I added // ??? in the code where a change might be required.. – Marzz Oct 20 '20 at 00:59
  • No, I think class C is correctly instantiated. It's just that the member variable vec of class B is not instantiated in the constructor of class B. You need to add vec() in the initialization list. – Anonymous1847 Oct 20 '20 at 01:29
  • I don't understand why that would solve the problem? For example, if I change the code such that class B passes the beta-vector by value to class C, and class C stores beta as a member variable instead of a pointer, the beta values are printed correctly. The problem is that updating the beta vector in class B would not affect the output of the PrintBetas() member function. Could you please provide the syntax for correctly initializing the vec member of class B then? Thank you! – Marzz Oct 20 '20 at 08:09
  • You have this line: `B( VectorXd beta_IN, int nr_C ): beta( beta_IN ) {`. Make it into: `B( VectorXd beta_IN, int nr_C ): beta( beta_IN ), vec() {`. You weren't initializing vec(). I think generally that causes a compiler warning, you should probably turn those on. However, I've realized there's also another problem, which is that if a B is copied, then the Cs in that new B will still point to the beta in the old B. I'll edit my answer soon. – Anonymous1847 Oct 20 '20 at 17:56
  • I tried your suggested solution but it didn't work, i.e. the PrintBetas() function still does not print the VectorXd beta values nr_C times on the screen. You're right, you'd need a copy constructor but in my case the class hierarchy is initialized once and not changed afterwards, hence I kept the example as simple as possible. – Marzz Oct 20 '20 at 18:18
  • @Marzz I understand what you mean now, I updated my answer. – Anonymous1847 Oct 20 '20 at 20:22
  • @ Anonymous: hugely appreciate your help, but could you please explain where in my example code the ```B``` class is being copied, and hence why adding a copy instructor would help initialize the member pointers in the ```C``` class? The only question I have is how to get the member pointers in ```C``` to point to the member variable VectorXd beta in ```B```, so that the member function PrintBetas() prints out the current value of VectorXd beta nr_C times. – Marzz Oct 21 '20 at 20:28
  • @Marzz Well you see, in your A class you have a vector of Bs. Whenever you have a vector you should assume the vector elements are going to get copied or moved, say when the vector has to reallocate, or even when you're inserting into them. Hence, making the Bs into B pointers would remove the copying and moving of the B objects themselves. – Anonymous1847 Oct 21 '20 at 22:38
  • @Marzz You copy objects of type `B` with the line `subclass.push_back( B( beta, nr_C) );`. You create a temporary `B` object, then push a **copy** of that temporary into the `subclass` vector. – JaMiT Oct 21 '20 at 23:53
  • @JaMiT: thank you for the explanation! It makes perfect sense now. Anonymous1847: would you perhaps be able to edit your post to include this explanation? I added the copy constructor and verified that it was the correct solution - thank you for your help! – Marzz Oct 22 '20 at 12:29