tags:

views:

137

answers:

4

Hi, I'm a PHP developer trying to write some C++.

I'm having trouble with assigning an object as an another object's property. In PHP, I'd write this:

class A {
    public $b;
}
class B {

}

$a = new A;
$a->b = new B;

How do I do that in C++? I got this so far:

class A {
    B b;
public:
    void setB(&B);
};
class B {

};

void A::setB(B &b)
{
    this->b = b;
};

A * a = new A();
B * b = new B();
a->setB(b);

What am I doing wrong?

+2  A: 
  1. Instead of &B, you mean B&.

    class A {
        B b;
    public:
        void setB(B&); //<--
    };
    
  2. A pointer cannot be implicitly dereferenced. So a->setB(b) won't compile. You need to write a->setB(*b).

  3. You don't need new to construct an object. For example, this works:

    A a;
    B b;
    a.setB(b);
    
  4. Don't use idioms from other languages directly in C++. For example, setters and getters are seldom needed. In fact, your A class could just be a simple struct.

KennyTM
seh
+6  A: 

Just do this:

class B 
{
};

class A
{
    B b;
};


int main()
{
   A  anA;  // creates an A. With an internal member of type B called b.

   // If you want a pointer (ie using new.
   // Then put it in a smart pointer.
   std::auto_ptr<A>  aPtr = new A();
}

You don't actually need to create the B separately. The B b is part of the class and is created (using the default constructor) automatically when the A object is created. Creating the two objects seprately and then combining them is a bad idea.

If you want to pass some parameters to the B object as it is constructed. That is easy to do by creating a constructor for A that calls B's constructor:

class B
{
    public:
      B(std::string const& data)    // The B type takes a string as it is constructed.
        :myData(data)               // Store the input data in a member variable.
      {}
    private:
      std::string myData;
};
class A
{
    public:
      A(std::string const& bData)   // parameter passed to A constructor
          :b(bData);                // Forward this parameter to `b` constructor (see above)
      {}
    private:
      B  b;
};

int main()
{
    A  a("Hi there");  // "Hi there" now stored in a.b.myData
}
Martin York
+1  A: 

A couple changes will make it compile:
1. class B needs to be declared before A so that it can be used in class A
2. The declaration setB(&B) needs a minor change to setB(B&)

class B {
};

class A {
    B b;
public:
    void setB(B&);
};
void A::setB(B &b)
{
    this->b = b;
};

int main ()
{   
A * a = new A();
B * b = new B();
a->setB(*b);
}   

To make it more efficient consider the adding the following constructor that accepts B as an argument and the initializes the member variable 'b'. This will use a copy constructor on the 'b' member variable instead of using the default constructor and then the assignment operator.

A(B& b_) : b(b_) 
{
}
skimobear
Your code still won't compile (you are giving a pointer to a function that takes a reference instead) and if it would, the memory leak would still be there.
Jasper
skimobear
A: 

There are a lot of things wrong with this code:

  • As KennyTM notes, the ampersand is in the wrong place.
  • You are passing a B* to a function that takes a B&, they aren't the same in C++
  • The design shown in your php fragment appears to misdesigned. While what you are doing is something you might want to do at times, you'll usually want to use a constructor instead, C++ and PHP alike.
  • You are putting code directly in the file, this isn't allowed in C++, put it in the main function instead
  • memory management: you are using new without delete (or a smart pointer class)
  • You are using class B in class A while class A doesn't know about class B (it is defined later) - you should put class B on top (or perhaps use forward declaration)

A few ways to make your code work properly are shown below.

include <memory>
using std::auto_ptr;


class B
{
}

class A
{
public:
    A();
    SetB(B& b);

private:
    B b1;    // a B made in the constructor
    B b2;    // a COPY of the original B you make
}

A::A()
   : b1(/*whatever you want to pass to B's constructor*/)
{
}

A::SetB(B& b)
{
    b2 = b;
}

int main(int agrc, char** argv)
{
    A firstA();
    B firstB();

    firstA.SetB(firstB);

    A* secondA = new A();
    B* secondB = new B();

    secondA->SetB(*secondB);

    auto_ptr<A> thirdA(new A());
    auto_ptr<B> thirdB(new B());

    thirdA->SetB(*thirdB);

    // whatever other code

    delete secondA;
    delete secondB;
}

Note that id you call SetB only once (and there is no problem with cyclic depndencies between the different objects you are creating), but you do want the flexibility of constructing the B outside the class, you can instead make a parameter of the constructor.

Also note that you are making a copy of the B you create in main - if you want to use the came copy in the class (as you would in PHP), you should use a member reference (in which case you will need to set what it refers to in the constructor, you can't do so in the SetB function.

Then, note that there are serious problems with the secondA, secondB approach and as such it is not recommended (above nothing will go wrong, however, it is easy to get code that leaks memory this way (and hard to find out where the leak is) and on top of that when you want to use exceptions, you will need exception safe code, which is not achievable using plain old pointers.

Lastly, note that you mix and match here. There is no problem at all with using firstA and thirdB together, for example.

Jasper