Thread: C++, classes - a couple of questions

  1. #1
    Registered User
    Join Date
    May 2011
    Posts
    6

    C++, classes - a couple of questions

    Hi

    I've been reading about c++ for a while but I have to say that I lack hands on experience.

    I want to create 3 objects; car, mainPart, subPart in shuch a way that I can write something like this:
    Code:
    car.mainPart[11].subPart[33].someFunction
    basically 'car' has a number of mainParts, and each mainPart has a number of subParts. The number of elements in the mainPart and subPart arrays are defined by the user at runtime.

    it is possible to do the above without exposing the member of mainPart and subPart?
    If I have to expose the members using 'public:' is it better to use a structure instead of a class?
    Is there a better way to implement the above (maybe y using inheritance)? if yes then how?

    Thanks for the help.
    Last edited by naton; 05-04-2011 at 07:40 AM.

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by naton View Post
    it is possible to do the above without exposing the member of mainPart and subPart?
    No. To use it like you have described, mainPart needs to be a public member of car. It also needs to support an operator[], so you can select the particular part. Each main part needs to have a member named subPart which also needs to support an operator[] that gives a reference to a sub part.

    Quote Originally Posted by naton View Post
    If I have to expose the members using 'public:' is it better to use a structure instead of a class?
    If you can think of no option that doesn't involve your class exposing its members, then you might as well make everything public.

    However, the art of designing your objects and classes is providing as much access as is needed, but not providing more than necessary. Making everything public will certainly ensure access if provided but may be to permissive.

    There are many alternate ways of doing things which all have benefits and trade-offs for particular applications. So the "best" for your program may not be "best" for a program that I write, or vice versa.

    The first thing you need to think about is what syntax is necessary to call someFunction(). For example, this
    Code:
         AsubPart part = car.GetSubPart(11, 33);
         part.someFunction();
         car.SetSubPart(11,33);
    may take three lines, but allows the car a bit more control over its internals (GetSubPart() and SetSubPart() can do error checking that is not possible with your approach.

    An alternative approach for the caller may be
    Code:
         car.ManipulateSubPart(11, 33, &SubPart::someFunction);
    which means that ManipulateSubPart basically finds the right part, and invokes a supplied member function on it. This function can check that the values 11 and 33 are valid, and only invoke someFunction() if they are.

    Quote Originally Posted by naton View Post
    Is there a better way to implement the above (maybe y using inheritance)? if yes then how?
    Inheritance does not address this problem at all. There is more to OO than inheritance, despite the fact that introductory OO courses teach inheritance in some depth, but often neglect other things.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  3. #3
    Registered User
    Join Date
    May 2011
    Posts
    6
    Hi grumpy, and thanks for the detailled clarrification.

    what do you mean exactly by the operator[]? do you mean that I should overload this operator[] so I can manipulate mainParts and subParts using the array syntax?

    When I posted this thread the design of my classes was not really clear in my head. So I used cars and parts as an example. What I want to do is the following. I wrote a couple of years ago an artificial neural network program (a perceptron network) with two layers and several nodes in VBA for AutoCAD. What I'm trying to do now is to rewrite the program in C++ and objectARX for AutoCAD. ObjectARX is basically AutoCAD's API.

    Forget about the car example. I have three classes: Perceptron, Layer, Neuron.
    The Perceptron has 2 layers and each layer has some neurons (known at runtime). The number of neurons is different for each layer. In the below layer[] and neuron[] are one dimentioanl arrays. So it's:
    Perceptron.Layer[0].Neuron[] and
    Perceptron.Layer[1].Neuron[]

    I given the classes design some thought yesterday and it's OK to have layer[] in the public part of the layer class since all the data (input, output, and weight) is stored and manipulated by the neuron objects.

    My ultimate goal is to have a general network, in the sense that all I have to do in the future to convert the perceptron network into another network (back propagation network) is to modify the learning algorithm in the neuron class. Or keep the neuron class as it is an apply the changes to the perceptron class instead.

    I'm still undecided regarding if I should implement the training algorithm in the neuron class or in the perceptron class... in VBA I didn't have to worry about all this. I have only a perceptron class, and the layers and neurons were defined as a data type (equivalent to a structure in C++). Maybe I should write the C++ exactly as I did it in VBA.

    I started codding the classes by I haven't finished them yet. I'll post the code when I'm done for further discussion.
    Last edited by naton; 05-06-2011 at 04:16 AM.

  4. #4
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by naton View Post
    what do you mean exactly by the operator[]?
    Look up (for example, using google) operator overloading in C++.

    If you wish to access some parts of an object using array syntax such as
    Code:
        SomeType some_object;
        x = some_object[3];
        some_object[4] = x;
    then there needs to be an appropriate operator[] function supplied that works for SomeType. Importantly, however, it does not always make sense for a class to support this type of usage.

    Quote Originally Posted by naton View Post
    do you mean that I should overload this operator[] so I can manipulate mainParts and subParts using the array syntax?
    It is, if you insist on using array syntax as per the example in your original post.

    There are trade-offs of doing that, and also alternative approaches that are often safer, more robust, etc. I noted a couple of those - in rough terms - in my previous post.

    Quote Originally Posted by naton View Post
    I given the classes design some thought yesterday and it's OK to have layer[] in the public part of the layer class since all the data (input, output, and weight) is stored and manipulated by the neuron objects.
    From your description, I would disagree.

    Providing access to private members of a layer would allow any neuron to affect that layer. For example, would you want a neuron in one layer to have ability to simulate a completely different layer? If not, then you need to find an alternative approach that only allows a layer to be affected by neurons within it.

    The mistake you are making is just mapping code from VBA to C++. At one level that works, for getting things going. But mapping from one language to another usually involves redesign, particularly if the new language provides powerful options not available in the first. Otherwise you gain very little by changing language. And I can assure you C++ makes a lot of design options available to you that are not available in VBA (the reverse is also true: if you were taking C++ code and reimplementing it in VBA, you would also need to redesign).
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  5. #5
    Registered User
    Join Date
    May 2011
    Posts
    6
    And I can assure you C++ makes a lot of design options available to you that are not available in VBA (the reverse is also true: if you were taking C++ code and reimplementing it in VBA, you would also need to redesign).
    I know

    I will put something together this weekend and list it next week. This will help me put things into perspective.

    Thanks again and have a good weekend.

  6. #6
    Registered User
    Join Date
    May 2011
    Posts
    6
    Okay. This is the code for the three classes: neuron, layer and perceptron. I haven't finished the perceptron class yet, and it is likely that I'll have to tweak the layer class too. I've implemented the operator[] but I'm not sure if what I did makes sense.

    Let me know the areas where I can improve the code... thanks

    Code:
    // neuron.h
    ////////////////////////////////
    #ifndef __NEURON_H_
    #define __NEURON_H_
    
    class neuron
    {
    	public:
    		enum typeOfWeight {kFIXED, kVARIABLE};
    
    		neuron();
    		neuron(double *pInputs, typeOfWeight type, int numberOfInputs);
    		~neuron();
    		
    		void setNode (double *pInputs, typeOfWeight type, int numberOfInputs);
    		void setInputs(double *pInputs);
    		void setNumOfInputs(int numInConnections);
    		void setWeights(typeOfWeight type);
    
    		double getOutput(void);
    
    		void updateWeights(void);
    
    	protected:
    
    	private:
    		double *pmInputs;
    		double *pmWeights;
    		double mOutput;
    		int mNumInConnections;
    		
    		double rndWeight(int const &min, int const &max);
    };
    
    #endif	\\ __NEURON_H_
    Code:
    // neuron.cpp
    ////////////////////////////////
    #include "neuron.h"
    #include <iostream>
    #include <stdlib.h>			// required to call rand() function
    #include <time.h>			// used to return the current time - used 
    							// as seed for the rand() function 
    
    neuron::neuron()
    {
    
    }
    
    neuron::neuron(double *pInputs, typeOfWeight type, int numberOfInputs)
    {
    	int i = 0;
    
    	mNumInConnections = numberOfInputs;
    
    	pmInputs = new double[mNumInConnections];
    	pmWeights = new double[mNumInConnections];
    
    	// set inputs
    	for (i = 0; i < mNumInConnections; i++)
    	{
    		//*(pmInputs + i) = *(pInputs + i);
    		pmInputs[i] = pInputs[i];
    	}
    
    	// set weights
    	setWeights(type);
    }
    
    neuron::~neuron()
    {
    	// free the memory allocated by 'new double[]'
    	delete[] pmInputs;
    	pmInputs = 0;
    
    	delete[] pmWeights;
    	pmWeights = 0;
    }
    
    
    void neuron::setNode (double *pInputs, typeOfWeight type, int numberOfInputs)
    {
    	int i = 0;
    
    	mNumInConnections = numberOfInputs;
    
    	pmInputs = new double[mNumInConnections];
    	pmWeights = new double[mNumInConnections];
    
    	// set inputs
    	for (i = 0; i < mNumInConnections; i++)
    	{
    		//*(pmInputs + i) = *(pInputs + i);
    		pmInputs[i] = pInputs[i];
    	}
    
    	// set weights
    	setWeights(type);
    }
    
    void neuron::setInputs(double *pInputs)
    {
    	int i = 0;
    
    	pmInputs = new double[mNumInConnections];
    
    	// set inputs
    	for (i = 0; i < mNumInConnections; i++)
    	{
    		//*(pmInputs + i) = *(pInputs + i);
    		pmInputs[i] = pInputs[i];
    	}
    }
    
    void neuron::setNumOfInputs(int numInConnections)
    {
    	mNumInConnections = numInConnections;
    }
    
    void neuron::setWeights(typeOfWeight type)
    {
    	int i = 0;
    
    	if (type == kFIXED)
    	{
    		for (i = 0; i < mNumInConnections; i++)
    		{
    			//*(pmWeights + i) = *(pWeights + i);
    			pmWeights[i] = 1;
    		}
    	}
    
    	if (type == kVARIABLE)
    	{
    		for (i = 0; i < mNumInConnections; i++)
    		{
    			//*(pmWeights + i) = *(pWeights + i);
    			pmWeights[i] = rndWeight(0, 1);
    		}
    	}
    }
    
    double neuron::getOutput(void)
    {
    	int i = 0;
    	double sum = 0;
    	double input = 0;
    	double weight = 0;
    
    	// calculate the sum of inputs x weights
    	for (i = 0; i < mNumInConnections; i++)
    	{
    		//input = *(pmInputs + i);
    		//weight = *(pmWeights + i);
    		input = pmInputs[i];
    		weight = pmWeights[i];
    
    		sum = sum + (input * weight);
    	}
    	
    	// assess the neuron output
    	if (sum > 0)
    		mOutput = 1;
    
    	if (sum <= 0)
    		mOutput = 0;
    
    	// return the result of the calculation
    	return mOutput;
    }
    
    double neuron::rndWeight(int const &min, int const &max)
    {
    	// RAND_MAX = 0x7fff = 32767 --- defined in <stdlib.h>
    	// below is: min + ((rand() / RAND_MAX) * (max - min))
    	// the expression was divided into a, b, c so to avoid integer type cast;
    	double a = double(rand()) / double(RAND_MAX);
    	double b = double(max - min);	
    	double c = double(a * b);
    	double expression = min + c;
    	
    	//acutPrintf("\nexpression: %f", expression);	// for debugging
    	return expression;
    }
    Code:
    // layer.h
    ////////////////////////////////
    #ifndef __LAYER_H_
    #define __LAYER_H_
    
    #include "neuron.h"
    
    class layer
    {
    	public:
    		layer();
    		layer(int numOfNodes, int numberOfInputs, double *inputs, neuron::typeOfWeight type);
    		~layer();
    		
    		void setNodeInputs(double *pInputs, int nodeIndex);
    		void setNumOfNodes(int numNodes);
    
    		double getNodeOutput(int nodeIndex);
    
    		neuron &operator[](int index);					// if reference '&' I can use the '.' operator
    														// if pointer '*' I will have to use '->' operator 
    
    	protected:
    
    	private:
    		int mNumOfNodes;
    		int mNumOfInputs;
    		double *pmInputs;
    		neuron *pmNeurons;
    };
    
    #endif	// __LAYER_H_
    Code:
    // layer.cpp
    ////////////////////////////////
    #include "layer.h"
    #include <iostream>
    
    layer::layer()
    {
    // not implemented yet
    }
    
    layer::layer(int numOfNodes, int numberOfInputs, double *inputs, neuron::typeOfWeight type)
    {
    	int i = 0;
    
    	mNumOfNodes = numOfNodes;
    	mNumOfInputs = numberOfInputs;
    	pmInputs = inputs;
    	
    	pmNeurons = new neuron[mNumOfNodes]();
    
    	for (i = 0; i < mNumOfNodes; i++)
    	{
    		pmNeurons[i].setNode(pmInputs, type, numberOfInputs);
    	}
    }
    
    layer::~layer()
    {
    	delete [] pmNeurons;
    	pmNeurons = 0;
    }
    
    void layer::setNodeInputs(double *pInputs, int nodeIndex)
    {
    
    }
    
    void layer::setNumOfNodes(int numNodes)
    {
    	mNumOfNodes = numNodes;
    
    	pmNeurons = new neuron[mNumOfNodes]
    
    }
    
    neuron& layer::operator[](int index)
    {
    	if (index > mNumOfNodes)
    		std::cout << "ERROR...! neuron.operator[]\n\n";
    	else
    		return pmNeurons[index];
    }
    Code:
    // perceptron.h
    ////////////////////////////////
    #ifndef __PERCEPTRON_H_
    #define __PERCEPTRON_H_
    
    #include "layer.h"
    #include <queue>
    
    class perceptron
    {
    	public:
    		perceptron(	int numLayers = 2, 
    					int numNodesLayer1 = 1, 
    					int numNodesLayer2 = 1);
    		~perceptron();
    
    		void training(queue pair);			// pair is a queue containing inputs and their targets.
    									// inputs and targets can be part of a structure.
    									// inputs are arrays of double.
    									// targets are array of binary numbers (0s or 1s).
    
    		layer &operator[](int index);
    
    	protected:
    
    	private:
    		int mNumOfLayers;
    		layer *pmLayers;
    		int mNumTrainingPatterns;
    };
    
    #endif	// __PERCEPTRON_H_
    Code:
    // perceptron.cpp
    ////////////////////////////////
    #include "perceptron.h"
    #include <iostream>
    
    perceptron::perceptron(int numLayers, 
    					   int numNodesLayer1, 
    					   int numNodesLayer2)
    {
    	mNumOfLayers = numLayers;
    	layer *pmLayers = new layer[mNumOfLayers];
    	
    	pmLayers[0].setNumOfNodes(pLayer1Inputs, i);
    	pmLayers[1].setNumOfNodes(pLayer2Inputs, i);
    }
    
    perceptron::~perceptron()
    {
    	delete [] pmLayers;
    	pmLayers = 0;
    }
    
    
    // pair is a queue containing inputs and their targets.
    // inputs and targets can be part of a structure.
    // inputs are arrays of double.
    // targets are array od binary numbers (0s or 1s).
    perceptron::training(queue pair)		
    {
    	int iterator = 0;
    	int count = 0;
    	
    	// initialise network's size and initial weights
    
    	// Start the training loop
    
    		// populate the network input layer with the next pattern to learn.
    
    		// calculate the input layer's nodes output. Note that the nodes in the 
    		// input layer are not processing nodes. Hence, nodes.output = node.input.
    
    		// calculate the output of the nodes in the output layer.
    
    		// update the weight of the connections between the nodes in the input 
    		// layer and the output layer.
    
    		// check the network average error
    			// calculate the error for the pattern used for training
    
    			// if the network has iterated through all the objects in the pool:
    			//	1.	Calculate the network average error for all the patterns.
    			//	2.	Store the average in a vector (i.e. dynamic array) so
    			//		so it can be used to analyse the performance of the network.
    			//	3.	If average <= threshold exit training 
    			//		else continue training
    
    }
    
    layer& perceptron::operator[](int index)
    {
    	if (index > mNumOfLayers)
    		std::cout << "ERROR...! layer.operator[]\n\n";
    	else
    		return pmLayers[index];
    }

  7. #7
    Registered User hk_mp5kpdw's Avatar
    Join Date
    Jan 2002
    Location
    Northern Virginia/Washington DC Metropolitan Area
    Posts
    3,817
    Code:
    // neuron.h
    ////////////////////////////////
    #ifndef __NEURON_H_
    #define __NEURON_H_
    
    
    // layer.h
    ////////////////////////////////
    #ifndef __LAYER_H_
    #define __LAYER_H_
    
    
    // perceptron.h
    ////////////////////////////////
    #ifndef __PERCEPTRON_H_
    #define __PERCEPTRON_H_
    FYI, identifiers containing either:
    1) A single leading underscore followed by a capital letter or...
    2) Double leading underscores
    ...are reserved for implementation specific use.
    "Owners of dogs will have noticed that, if you provide them with food and water and shelter and affection, they will think you are god. Whereas owners of cats are compelled to realize that, if you provide them with food and water and shelter and affection, they draw the conclusion that they are gods."
    -Christopher Hitchens

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Why don't you use std::vector instead of manual memory allocation? Then you won't have to remember to delete or remember the size.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  9. #9
    Registered User
    Join Date
    May 2011
    Posts
    6
    Quote Originally Posted by hk_mp5kpdw View Post
    Code:
    // neuron.h
    ////////////////////////////////
    #ifndef __NEURON_H_
    #define __NEURON_H_
    
    
    // layer.h
    ////////////////////////////////
    #ifndef __LAYER_H_
    #define __LAYER_H_
    
    
    // perceptron.h
    ////////////////////////////////
    #ifndef __PERCEPTRON_H_
    #define __PERCEPTRON_H_
    FYI, identifiers containing either:
    1) A single leading underscore followed by a capital letter or...
    2) Double leading underscores
    ...are reserved for implementation specific use.
    What are they reserved for?
    Thanks

    Elysia
    Why don't you use std::vector instead of manual memory allocation? Then you won't have to remember to delete or remember the size.
    Because I want to exercise with pointers and pointers manipulation

  10. #10
    Registered User hk_mp5kpdw's Avatar
    Join Date
    Jan 2002
    Location
    Northern Virginia/Washington DC Metropolitan Area
    Posts
    3,817
    Quote Originally Posted by naton View Post
    What are they reserved for?
    Like I said, for implementation specific use. That means individual compiler vendor's may or may not have an identifier they use that is specific to their implementation that has the potential to conflict with something you put in place if you happen to use identifiers with those same rules. Thus, you should avoid doing that.
    "Owners of dogs will have noticed that, if you provide them with food and water and shelter and affection, they will think you are god. Whereas owners of cats are compelled to realize that, if you provide them with food and water and shelter and affection, they draw the conclusion that they are gods."
    -Christopher Hitchens

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by naton View Post
    Because I want to exercise with pointers and pointers manipulation
    A fair reason. I might also suggest you learn smart pointers and apply them here for some extra bonus.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Registered User
    Join Date
    May 2011
    Posts
    6
    Quote Originally Posted by hk_mp5kpdw View Post
    Like I said, for implementation specific use. That means individual compiler vendor's may or may not have an identifier they use that is specific to their implementation that has the potential to conflict with something you put in place if you happen to use identifiers with those same rules. Thus, you should avoid doing that.
    thanks.

    Elysia
    Originally Posted by naton
    Because I want to exercise with pointers and pointers manipulation
    A fair reason. I might also suggest you learn smart pointers and apply them here for some extra bonus.
    I'll give it a go...thanks

    How about the code? any recommendations?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Couple of Questions About Classes
    By bengreenwood in forum C++ Programming
    Replies: 3
    Last Post: 05-20-2009, 02:50 PM
  2. A couple of questions regarding classes and strings.
    By Azimuth in forum C++ Programming
    Replies: 15
    Last Post: 06-12-2004, 10:47 AM
  3. A couple of OOP questions
    By codec in forum C++ Programming
    Replies: 5
    Last Post: 05-03-2004, 07:18 PM
  4. Couple C questions :)
    By Divx in forum C Programming
    Replies: 5
    Last Post: 01-28-2003, 01:10 AM
  5. couple questions
    By Labelizm in forum Windows Programming
    Replies: 3
    Last Post: 07-23-2002, 05:21 AM