vectors and derived classes

Pages: 12
Thanks for all of you the explanation
now when you say subclasses, do you mean the objects that the pointers in the vector point to? you sure can :P ! Say you have an object like this.

1
2
3
4
5
6
7
8
9
10
11
12
class c
{
public:
      int foo;
};

int main()
{
      c instance;
      instance.foo = 3;   // etc just access like normal
      return 0;
}


now, instead of having a normal object, you have a pointer to the object, then you just do this to do the same thing.

1
2
3
4
5
6
7
8
int main()
{
       c * instance;
       instance = new c;
       instance->foo = 3;  // the   ->  operator means "this member of the object pointed to by"
       delete instance;
       return 0;
}


so you just apply that operator to the vector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
       vector<c *> instances;
       instances.push_back(new c);
       instances.push_back(new c);

       instances[0]->foo = 2;
       instances[1]->foo = 3;

       delete instances[0];
       delete instances[1];
       
       return 0;
}


or when you say subclasses do you mean be able to access objects within the objects in the vector? then you would just do this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class c
{
 public:
      int foo;
};

class b
{
       c otherClass;
};

int main()
{
       vector<b *> instances;
       instances.push_back(new b);
       instances.push_back(new b);

       instances[0]->otherClass.foo = 3;
       instances[1]->otherClass.foo = 3;

       delete instances[0];
       delete instances[1];
      
       return 0;
}
Last edited on
That's not quite what I meant. If I create a vector of pointers to a parent class, then I can't use those pointers to access derived classes, can I? At least, that's my interpretation of what I'm reading in my (old) reference book.

EDIT:

Here's what I'm looking at doing now:

1
2
3
4
5
class DemodNyqCell
{
private:
	vector<NyqMultBlock *>		blocks;
...


1
2
3
4
5
6
7
8
9
10
DemodNyqCell::DemodNyqCell(int32_t rv)    // default constructor
	: sum(rv), resetValue(rv)
{
	NyqMultBlock*	pBlock;
	blocks.reserve(DEMOD_NYQ_NBR_BLOCKS);
	for (int i = 0; i <= DEMOD_NYQ_NBR_BLOCKS; ++i)
	{
		pBlock = new (NyqMultBlock);
		*pBlock = NyqMultBlock(rv);
		blocks.push_back(pBlock);


But my concern is that, since the pointers are pointers to the base class, I can't use them to point to derived classes. True?
Last edited on
Ah ha, here comes the fun. That's what polymorphism is all about. pointers of a base class can point to AND be used to access objects derived from it. you just have to make sure that all the functions that are redefined in the derived class from the base class are declared as virtual and use the protected or public access specifier. If you look back at my first post, at my first example, this is shown. The members that were public or protected were inherited by the derived class, and since they were originally declared in the base class, the virtual function could be called from a pointer of base class to an object of derived class , but call the derived classes version of that function instead. Now, you can't access any new functions in the derived objects that aren't in the base class from a base class pointer, but since your were wanting to put these two classes in a vector anyway, i assume the objects are treated the same with the same functions.

EDIT:
if it isn't too much trouble, might you be able to post these whole two objects and the object containing the vector? I might be able to help you better through example :P
Last edited on
Posting the complete objects would take a lot of space, and give a lot of unnecessary information, so I'll post snippets, if that's OK. But first, a (long overdue) explanation:

The complete data structure is several classes deep. A given container class, upon construction, needs to invoke the constructors of the classes it contains. (We're not talking parent/sub class relationship; just a heirarchy to the data structure.)

The first wrinkle in this is that one class, the DemodNyq, will contain a vector of objects of the class DemodNyqCell. DemodNyqCell has a vector containing a combination of (parent class) NyqMultBlock and (subclass) NmbDelay elements.

HOWEVER:

Not all DemodNyq objects will contain the same combination of these two classes. Some will have 8 of one, and 0 of the other, others will have different combinations. I have tried to solve this by passing an argument to the DemodNyqCell constructor, telling it how many to make of each.

So, with this as background (I hope it made sense), here's some code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class DemodNyq {
private:
	vector<DemodNyqCell>	cells;
	vector<int32_t>			inpBuffer;
	vector<int32_t>			coeffBuffer;
public:
	DemodNyq(int32_t rv);
	DemodNyq(const DemodNyq &d);
	~DemodNyq();
.
.
.
DemodNyq::DemodNyq(int32_t rv)
{
	int32_t		delayCount;					// how many of the NyqMultBlocks contain delays
	DemodNyqCell* pCell;

	cells.reserve(DEM_NYQ_NBR_CELLS);
	for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
	{
		delayCount = min(16 - i, 8);
		pCell = new (DemodNyqCell);

		*pCell = DemodNyqCell(rv, delayCount);
		cells.push_back(*pCell);
	}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class DemodNyqCell
{
private:
	vector<NyqMultBlock *>		blocks;
	FlipFlopReg32				sum;
	int32_t						resetValue;
public:
	DemodNyqCell(int32_t rv = 0, int32_t delay = 0);		// default constructor
.
.
.
DemodNyqCell::DemodNyqCell(int32_t rv, int32_t delay)    // default constructor
	: sum(rv), resetValue(rv)
{
	NyqMultBlock*	pBlock;
	blocks.reserve(DEMOD_NYQ_NBR_BLOCKS);
	for (int i = 0; i <= DEMOD_NYQ_NBR_BLOCKS; ++i)
	{
		if (i < delay)
			pBlock = new (NmbDelay);
		else
			pBlock = new (NyqMultBlock);
		*pBlock = NyqMultBlock(rv);
		blocks.push_back(pBlock);
	}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class NyqMultBlock		// base class; no delay register
{
protected:
	FlipFlopReg32	regProduct;
public:
	NyqMultBlock(int32_t rv=0);				//  constructor w/ reset parameter
	NyqMultBlock (const NyqMultBlock &nmb);	// copy constructor
	~NyqMultBlock();							// destructor
	void cycle(int32_t	inp,
			   int32_t	coeff,
			   bool		clockEnable,
			   bool		resetFlag);
	void reset();
	void display();
	int32_t get();
};

class NmbDelay : public NyqMultBlock	// inherited class w/ delay register.
{
private:
	FlipFlopReg32	regDelay;
public:
	NmbDelay(int32_t rv=0);
	NmbDelay(const NmbDelay &nmbd);
	~NmbDelay();
	void cycle(int32_t	inp,
			   int32_t	coeff,
			   bool		clockEnable,
			   bool		resetFlag);
	void reset();
	void display();
};

// I can include the code as well for this, if you're interested. 


This at least compiles and survives the constructions; I haven't exercised it enough yet to know if it works right.
k, i get what your trying to do here, and the setup for your derived and base class, can work out perfectly. I assume that the reset, display, and cycle methods are different in NmdDelay than they are in NyqMultBlock, but since they are the same name and share the same number of parameters, you can totally polymorph this. Just augment the NyqMultBlock class a bit like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class NyqMultBlock		// base class; no delay register
{
protected:
	FlipFlopReg32	regProduct;
public:
	NyqMultBlock(int32_t rv=0);				//  constructor w/ reset parameter
	NyqMultBlock (const NyqMultBlock &nmb);	// copy constructor
	virtual ~NyqMultBlock();							// destructor
	virtual void cycle(int32_t	inp,
			   int32_t	coeff,
			   bool		clockEnable,
			   bool		resetFlag);
	virtual void reset();
	virtual void display();
	int32_t get();  /* since this function is just the same in both, there is no reason to declare it virtual because there's no need to change it in the derived class*/
};


You don't really need to do anything to your derived class, NmbDelay there. for the record, NmbDelay will also inherit private member FlipFlopReg32 regproduct, so just a heads up if you wanted to do that or not. Now the constructor in your DemodNyqCell will work properly without any changes. to call a the functions of the objects in the vector, all you have to do is , for example, do this:

blocks[0]->reset();

and whether it is of the base class or the derived class, that classes version of the functions is called

EDIT: by the way, when approaching something that may need to have 2 or more different classes that all need to be called and used through the same methods, you might also consider what some people call an interface. You basically make a dummy class that will never actually have an instance made of it, like you will never go new AbstractClass. You just make everything that will be called by this convention inherit from it, and then commit to never make new methods that are not privately called. You just play off the built in external methods. Just some programming techniques :)
Last edited on
Thanks, FC. So, even though I'm declaring those functions virtual, I can still use them? I got a different impression from the docs.

Also: in my constructors, where I'm using new and then doing a push_back on the contents...does that look OK? I ask for two reasons: one, I've never done that before, and two, my program is crashing in the destructors when I try to delete the memory allocated by new.

I appreciate your help through all of this.

EDIT: disregard my question about the destructors; I just realized that I'm doing shallow copies in my copy constructors. (This wasn't an issue until I introduced the pointers.)
Last edited on
No problem, this is one of my favorite outlets of object oriented programming :P .

declaring something virtual, is really just a way of telling the program that all inheriting classes are aloud to overwrite that function with there own version. Functionally (heh) it will work exactly the same as normal if an instance of the base class calls that function.

And no, there are a couple things wrong with the constructors. First look at this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cells.reserve(DEM_NYQ_NBR_CELLS); /* this reserves DEM_NYQ_NBR_CELLS(the number)
available slots in your vector. you actually don't want to do this because your using push back
which just creates one at the end and then fills it with whatever parameter you sent it. so say
you reserve 10 slots. then push_back will just create one at array position [10] and add that
object to it. not what you wanted, so just omit this */

for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
	{
		delayCount = min(16 - i, 8);
		pCell = new (DemodNyqCell); /* here you make a new object of DemodNyqCell and
have pCell point at it*/

		*pCell = DemodNyqCell(rv, delayCount); /* and then here you try and construct another new object over the top of it. ouch*/
		cells.push_back(*pCell);   
	}


i would revise that to be like this. first, change your cells vector to be of type DemodNyqCell *:

1
2
3
4
5
for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
	{
		delayCount = min(16 - i, 8); 
		cells.push_back(new DemodNyqCell(rv, delayCount));   
	}


and then just make sure that in your destructor of DemodNyq, you run a for loop from 0 to size of cells and delete each object.

More to come, need to make dinner real quick :)
k, similarly in DemodNyqCell, the constructor should be changed from this:

1
2
3
4
5
6
7
8
9
10
11
12
13
blocks.reserve(DEMOD_NYQ_NBR_BLOCKS);   // same problem as before
for (int i = 0; i <= DEMOD_NYQ_NBR_BLOCKS; ++i)
{
	if (i < delay)
		pBlock = new (NmbDelay);
	else
		pBlock = new (NyqMultBlock);
	*pBlock = NyqMultBlock(rv); /* right here is a problem. at pblock, you made a new
instance of either NmbDelay or NyqMultBlock, and then immediately constructed a new
instance of NyqMultBlock right over regardless of what it actually was. this should be 
omitted*/
	blocks.push_back(pBlock);
}

to something like this:

1
2
3
4
5
6
7
for (int i = 0; i <= DEMOD_NYQ_NBR_BLOCKS; ++i)
{
	if (i < delay)
		blocks.push_back( new NmbDelay(rv) );
	else
		bocks.push_back( new NyqMultBlock(rv) ;
}

and then do the same thing in the destructor as before.

EDIT: also just realized why maybe your delete commands were throwing up exceptions. When you dynamically allocated the the memory, and then constructed a non-dynamically allocated object right over it, that meant that when it got to the destructor, it was trying to dynamically delete a non-dynamic object, hence the errors. my revisions should clear this up :P
Last edited on
OK, I'm not sure I fully understand your push_back, but...it compiles, and runs (so far), so I'll go with it. Here's an update of the relevant constructors/destructors; I'd appreciate it if you'd give them a once-over.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
DemodNyq::DemodNyq(int32_t rv)
{
	int32_t		delayCount;					// how many of the NyqMultBlocks contain delays

	for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
	{
		delayCount = min(16 - i, 8);
		cells.push_back(new DemodNyqCell(rv, delayCount));
	}

	inpBuffer.reserve(DEM_NYQ_NBR_INPUTS);
	for (int i = 0; i < DEM_NYQ_NBR_INPUTS; ++i)
		inpBuffer.push_back(rv);

	coeffBuffer.reserve(DEM_NYQ_NBR_COEFFS);
	for (int i = 0; i < DEM_NYQ_NBR_COEFFS; ++i)
		coeffBuffer.push_back(rv);
}

DemodNyq::DemodNyq(const DemodNyq &dn)
{
	for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
	{
		cells.push_back(new DemodNyqCell);
		*(cells.at(i)) = *(dn.cells.at(i));
	}
	inpBuffer = dn.inpBuffer;
	coeffBuffer = dn.coeffBuffer;
}

DemodNyq::~DemodNyq() {
	for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
		delete cells.at(i);
	cells.clear();
	inpBuffer.clear();
	coeffBuffer.clear();
}


and:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
DemodNyqCell::DemodNyqCell(int32_t rv, int32_t delay)    // default constructor
	: sum(rv), resetValue(rv)
{
	for (int i = 0; i < DEMOD_NYQ_NBR_BLOCKS; ++i)
	{
		if (i < delay)
			blocks.push_back(new NmbDelay(rv));
		else
			blocks.push_back(new NyqMultBlock(rv));
	}
}
DemodNyqCell::DemodNyqCell			// copy constructor
(const DemodNyqCell &dnc)
{
	NyqMultBlock*	pBlock;
	for (int i = 0; i < DEMOD_NYQ_NBR_BLOCKS; ++i)
	{
		pBlock = new (NyqMultBlock);
		*pBlock = *(dnc.blocks.at(i));
	}
	sum = dnc.sum;
	resetValue = dnc.resetValue;
}
DemodNyqCell::~DemodNyqCell() 		// destructor
{
	for (int i = 0; i < DEMOD_NYQ_NBR_BLOCKS; ++i)
		delete blocks.at(i);

	blocks.clear();
}
k what push back actually does, in any type of vector, is just create a new value of the data type and then fill it with what parameter you pass it. For example.

1
2
3
4
5
6
int main()
{
      vector<int> foo;  // just creates the object. has no array elements currently
      foo.push_back(5) // first push_back creates a new instance of int, and then fills it with 5.
      return 0;
}


so with your array there, this is what i'm doing:

1
2
3
4
5
6
7
8
9
int main()
{
      vector<DemodNyqCell * > cells;
      cells.push_back( new DemodNyqCell);  /* this first creates a new instance of
DemodNyqCell * , so basically just creates a new pointer. and then fills ( or in this case,
creates the new object ) and then has that pointer point to it, all in one easy to manage
command :P */
      return 0;
}


now, in your constructor for DemodNyq, those other two vectors are using reserve as well and you may or not have wanted that so i'll point them out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DemodNyq::DemodNyq(int32_t rv)
{
	int32_t		delayCount;   // how many of the NyqMultBlocks contain delays

	for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
	{
		delayCount = min(16 - i, 8);
		cells.push_back(new DemodNyqCell(rv, delayCount));
	}

	inpBuffer.reserve(DEM_NYQ_NBR_INPUTS);        //makes this many new spots
	for (int i = 0; i < DEM_NYQ_NBR_INPUTS; ++i)   
		inpBuffer.push_back(rv);             // creates a new spot at end and fills

	coeffBuffer.reserve(DEM_NYQ_NBR_COEFFS);   //same
	for (int i = 0; i < DEM_NYQ_NBR_COEFFS; ++i)
		coeffBuffer.push_back(rv);
}


in the second constructor for DemodNyq, i'm not so sure your actually doing what you wanted:

1
2
3
4
5
6
7
8
9
10
DemodNyq::DemodNyq(const DemodNyq &dn)
{
	for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
	{
		cells.push_back(new DemodNyqCell); // new pointer, and new instance
		*(cells.at(i)) = *(dn.cells.at(i));  // overwrite instance with this one?
	}
	inpBuffer = dn.inpBuffer;
	coeffBuffer = dn.coeffBuffer;
}


MAYBE healthier like this? not at a compiler to see if this works out.

1
2
3
4
5
6
7
8
9
10
DemodNyq::DemodNyq(const DemodNyq &dn)
{
	for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
	{
		cells.push_back( *dn.cells.at(i));
                /* creates a new pointer, and then copy dn.cells.at(i) to the location it points at*/
	}
	inpBuffer = dn.inpBuffer;
	coeffBuffer = dn.coeffBuffer;
}




and then in that second construct of DemodNyqCell

1
2
3
4
5
6
7
8
9
10
11
12
DemodNyqCell::DemodNyqCell			// copy constructor
(const DemodNyqCell &dnc)
{
	NyqMultBlock*	pBlock;
	for (int i = 0; i < DEMOD_NYQ_NBR_BLOCKS; ++i)
	{
		pBlock = new (NyqMultBlock);     // brand new instance of NyqMultBlock
		*pBlock = *(dnc.blocks.at(i));      // then overwrite it with what this points to?
	}
	sum = dnc.sum;
	resetValue = dnc.resetValue;
}



Maybe you possibly meant to do something similar to what you did before?

1
2
3
4
5
6
7
8
9
10
11
DemodNyqCell::DemodNyqCell			// copy constructor
(const DemodNyqCell &dnc)
{
	NyqMultBlock*	pBlock;
	for (int i = 0; i < DEMOD_NYQ_NBR_BLOCKS; ++i)
	{
		blocks.push_back( *dnc.blocks.at(i) );
	}
	sum = dnc.sum;
	resetValue = dnc.resetValue;
}


that's about all i can see from this
Hey, FC: thanks for the suggestions. You were right about those reserve() calls; I'd forgotten to take them out.

The compiler didn't like this line:

cells.push_back( *dn.cells.at(i));

So I removed the "*" and it compiles and runs through one cycle. I still haven't really exercised the cycle() code yet, so I'm not 100% everything's working, but I'm definitely on the right track.

I really appreciate all your help. If you agree with the modification I mentioned above, I think I'm set for now. I'd be lying if I said I fully understood it all, but...I think I'm semi-functional at least.
actually, i'm a bit leary of that. Cause basically what that would do is make the new pointer in the vector point to the same object in dn.cells which isn't good, because if or when that other object is destroyed, the pointer in this vector now points to unallocated space, and if you try to access the object it no longer points to, that's hurt city right there. I think i would rather leave it like it was except kind like this:

1
2
3
4
5
6
7
NyqMultBlock * pBlock;
for (int i = 0; i < DEM_NYQ_NBR_CELLS; ++i)
	{
                pBlock = new NyqMultBlock;
                *pBlock= *dn.cells.at(i);
		cells.push_back(pBlock);
	}



anyway, np and good luck :)
Last edited on
OK, that makes sense. I'll go with the original implementation. Thanks again!
Topic archived. No new replies allowed.
Pages: 12