I have troubling addressing elements inside objects of Class3, please look at this simplified classes:
class Class1 {
public:
std::vector<Class2> c2v;
Class1();
};
class Class2 {
Class1 *instance;
int id;
public:
std::vector<Class3> c3v;
Class2 ( Class1 *instance, int id );
};
class Class3 {
Class1 *instance;
int id;
public:
Class3 ( Class1 *instance, int id );
};
And their constructors:
Class1::Class1()
{
for ( int i = 0; i < noi; ++i ) {
c2v.push_back ( Class2 ( this, i ) );
}
}
Class2::Class2 ( Class1 *instance, int id )
{
this->instance = instance;
this->id = id;
for ( int k = 0; k < nok; ++k ) {
c3v.push_back ( Class3 ( this->instance, k ) );
}
}
In main() an object of Class1 is instantiated with its default constructor. Therefore it creates a vector c2v and fills it with 'noi' objects of Class2.
At the same time, when the objects of Class2 are being put into c2v vector, they're instantiated and each one creates a vector c3v and fills it with 'non' objects of Class3.
The code compiles fine but at runtime when accessing public attributes of Class2 from Class3 objects (via this->instance->c2v[0].getSomeAttribute()
) the program stops with a EXC_BAD_ACCESS.
Inspecting with a debugger shows that the pointer to c2v[0]
gets corrupted (its value becomes 0x0).
I'm a newbie of C++ I was wondering what is the error when trying to instantiate vectors this way. Should I only declare the vectors and populate them in a separate function called after the creation of all instances of Class2 and Class3 is finished?
I'm adding some actual code, hope it won't be too long to read (please understand I've omitted some forward declarations and preprocessor directives):
// global variables
extern char *filename; //not used
extern int nodes;
extern int numProdotti;
extern int classe; //not used
extern int maxNumRange; //not used
extern int *numRanges;
extern int *list ;
extern int *typeProdMarket;
extern int totalQtyDemand; //not used
extern int totNumRanges; //not used
extern struct product {
int qty;
int cost;
} **prodMarket;
extern struct range {
int discount;
int startOfRange;
} **rangeMarket; //not used
int main ( int argc, char *argv[] )
{
Ctqd instance;
instance.runOpt();
}
class Ctqd {
void greedySimple();
void greedySimpleReverse();
void greedyFromY();
void greedyByNiceness1();
void greedyByNiceness2();
void greedyByStepTier1();
void greedyByStepTier2();
void randomLocalSearch1();
void LocalSearch2opt();
public:
std::vector<Item> items;
std::vector<Supplier> suppliers;
Solution *solution;
Ctqd();
~Ctqd();
void runOpt();
};
class Supplier {
Ctqd *instance;
int id;
int refTotQty;
int refDiscount;
int refDiscountTier;
double refTotPrice;
double refUnitPrice;
double niceness;
int purTotQty;
int purDiscount;
int purDiscountTier;
public:
std::vector<Offer> offers;
Supplier ( Ctqd *instance, int id );
int getId();
int getRefTotQty();
int getRefDiscount();
int getRefDiscountTier();
double getRefTotPrice();
double getRefUnitPrice();
double getNiceness();
int getPurTotQty();
int getPurDiscount();
int getPurDiscountTier();
void updateStats();
};
class Offer {
Supplier *supplier;
int id;
int refQty;
double refPrice;
double niceness;
int purQty;
public:
Offer ( Supplier *supplier, int id );
int getId();
int getRefQty();
double getRefPrice();
double getNiceness();
int getPurQty();
void setPurQty ( int qty );
int remainingQty();
};
Ctqd::Ctqd()
{
// constructing items vector
for ( int k = 0; k < numProdotti; ++k ) {
items.push_back ( Item ( this, k ) );
}
// constructing suppliers vector
for ( int i = 0; i < nodes; ++i ) {
suppliers.push_back ( Supplier ( this, i ) );
}
// building solution
solution = new Solution ( this );
}
Supplier::Supplier ( Ctqd *instance, int id )
{
this->instance = instance;
this->id = id;
// computing total reference quantity
refTotQty = 0;
for ( int k = 0; k < numProdotti ; ++k ) {
refTotQty += std::min ( list[ k ] , prodMarket[ this->id ][ k ].qty );
}
// computing reference discount coefficients
refDiscount = 0;
refDiscountTier = 0;
for ( int r = 0; r < numRanges[ this->id ]; ++r ) {
if ( refTotQty < rangeMarket[ this->id ][ r ].startOfRange ) {
break;
}
else {
refDiscount = rangeMarket[ this->id ][ r ].discount;
refDiscountTier = r;
}
}
//computing total reference price
refTotPrice = 0;
for ( int k = 0; k < numProdotti ; ++k ) {
refTotPrice += prodMarket[ this->id ][ k ].cost * std::min ( list[ k ] , prodMarket[ this->id ][ k ].qty );
}
refTotPrice = refTotPrice * ( 1.000 - refDiscount / 100.000 );
//computing reference unit price
refUnitPrice = refTotPrice / refTotQty;
//computing supplier niceness
niceness = refTotQty / refUnitPrice;
purTotQty = 0;
purDiscount = 0;
purDiscountTier = 0;
// building offers vector
for ( int k = 0; k < numProdotti; ++k ) {
offers.push_back ( Offer ( this, k ) );
}
}
Offer::Offer ( Supplier *supplier, int id )
{
this->supplier = supplier;
this->id = id;
// computing reference quantity
refQty = std::min ( list[ this->id ] , prodMarket[ this->supplier->getId() ][ this->id ].qty );
// computing reference price
refPrice = prodMarket[ this->supplier->getId() ][ this->id ].cost * ( 1.000 - this->supplier->getRefDiscount() / 100.000 );
// computing niceness of the offer
niceness = refQty / ( ( prodMarket[ this->supplier->getId() ][ this->id ].cost + refPrice ) / 2 );
// init purQty to 0
purQty = 0;
}
This is where I get the EXC_BAD_ACCESS:
int Offer::remainingQty()
{
return prodMarket[ supplier->getId() ][ id ].qty - purQty;
}
I've done some experiments: Changed the vectors in the Ctqd class and in the Supplier class to vectors of pointer to objects. Problem was solved only partially. Still had a EXC_BAD_ACCESS when constructing the offers objects.
The constructor for Offer class needs data from the Supplier object that created it. I thought that accessing that data while the suppliers vector was still being populated could lead to problems, so I created a little initialize() function in the supplier:
void Supplier::initialize()
{
// constructing offers vector
for ( int k = 0; k < numProdotti; ++k ) {
offers.push_back ( new Offer ( this->instance, id, k ) );
}
}
And added this at the end of the Ctqd class constructor:
// init newly built objects
for ( int i = 0; i < nodes; ++i ) {
suppliers[i]->initialize();
}
Now things seem to be working fine. But i still didn't figure out what the problem exactly was.