Good evening!

So I've been trying to get my head around these classes and it's not going well.

From what I've understand I've been calling functions correctly, but for some reason the members I'm creating are not being accepted.

class TheData
{
public:
	void InputData();
	void DisplayMembers();
	TheData();
	~TheData();
protected:
	double* Values;
	int Length;
	bool Valid;
};

TheData::TheData()
{
	Valid = false;
	Length = 0;
	Values = 0;
	TheMenu::MainMenu(); //Don't think that's right but dunno how else to call it
}

void TheMenu::MainMenu()
{
	cout << "Menu" << endl;
	cout << "-----------" << endl;
	cout << "1. Enter data for filtering" << endl;
	cout << "2. Enter filter values" << endl;
	cout << "3. Apply filter" << endl;

	char UserInput;
	cout << "Enter your option: ";
	cin >> UserInput;
	cout << endl;
	switch(UserInput) {
case '1':
	DataIn.InputData(); //That's the error
	DataOut.SendMembers(1,"false",0,0);
	break;
...

int main()
{
	TheData DataIn;
	return 0;
}
task2.cpp(73) : error C2065: 'DataIn' : undeclared identifier

So in summary what I understand that I've done is created an object of type class TheData in main(), and yet other functions don't accept that it exists. I thought that because main() doesn't end yet, all the members and variables that were created inside it would still be available to all the other functions in the program.

Kind regards,

Ed

Recommended Answers

All 10 Replies

>>I thought that because main() doesn't end yet, all the members and variables that were created inside it would still be available to all the other functions in the program.

You're logic is correct but there is another aspect that you are neglecting. Yes, variables that you declare in main are available as long as main has not finished (and that would be true for any function or { } block). However, the variables declared in the main function are not _visible_ or _accessible_ to any other parts of the program besides the main function. Visibility of variables have to be limited, otherwise it would be horribly difficult to make sure that you refer to the correct variables and also it introduces an crazy mix between the caller and callee contexts.

If you want access to a variable in a function it has to be either declared in that function, passed as a parameter to the function, or a global variable (or static data member of a class which is basically also a global variable). So these are the options:

//as a variable declared in the function:
void f() {
  int my_var = 42;
  cout << "my_var is: " << my_var << endl; //my_var is accessible in f() because it was declared in f().
};
int main() {
  f();
  return 0;
};

//as a passed parameter:
void f(int my_param) { //now my_param is a variable in f(), with the value my_var from main()
  cout << "my_param is: " << my_param << endl;
};
int main() {
  int my_var = 42; //variable in main()
  f(my_var);
  return 0;
};

//or as a global variable (this is bad style, but possible):
int my_var; //global variable;
void f() {
  cout << "my_var is: " << my_var << endl;
};
int main() {
  my_var = 42;
  f();
  return 0;
};

The last option is not considered good style, but it can be used if nothing else works. The first option is restrictive because it make the variable _only_ exist in the function that is called. The second option is usually the appropriate option. Note that in this case, the variable is passed-by-value which means that any modification to the value of "my_param" will not modify the value "my_var". If you want to pass a variable to a function and you want that function to be able to modify it, then you can pass-by-reference:

void f(int& my_param) { // note the & sign after the type, this indicates a reference
  my_param = 42; //this will modify the value of my_param but also that of my_var.
};

int main() {
  int my_var;
  f(my_var); 
  cout << "my_var is now: " << my_var << endl;
  return 0;
};

Now, for the call syntax in OOP. There are two types of member function of a class, static and non-static. A static member function has to be declared with the "static" keyword in front of it. A static function is related to the class, but is not attached to an instance of the class (i.e. it is not very different from a normal function, like f() in the above examples). One particularity of a static member function is that it has access to all private members of the class. To call a static member function, since it is not attached to an object (instance of a class), it only requires that you provide the name of the class with the scope resolution operator (two colons). So, for that, your syntax was correct:

TheMenu::MainMenu(); //this will call the static member function MainMenu() from the class TheMenu.

However, the default type of member functions is a non-static one (when "static" keyword is not present). These are often referred to as methods. They are special because they are automatically attached to an instance of the class they belong to (that instance is accessible through the "this" pointer). To call such as function, you need to call it from an instance of the class, so the above syntax for MainMenu() will not work for this. For example, if you wanted to call the DisplayMembers() function of TheData class in your main() function, you would do it like this:

int main()
{
  TheData DataIn; //this creates an instance (object) of the class TheData.
  DataIn.DisplayMembers(); //this will call method DisplayMembers() on the object DataIn.
  return 0;
}

And finally, if you have a pointer to the object instead of the object itself, you would use the -> instead of the dot to call the member function.

commented: So much information, I've learnt loads because of it!! +6

Wow, wasn't expecting that much information about it, but that's really helped me understand, thanks very much, Mike!
Could I use a contructor to pass the object by reference?

TheData::TheData()
{
	Valid = false;
	Length = 0;
	Values = 0;
	TheMenu::MainMenu(DataIn); // Am I right to pass it just by its name? Will the constructor know its name?
}

void TheMenu::MainMenu(TheData& DataIn) // I can still use the same name right?
{
	cout << "Menu" << endl;
	cout << "-----------" << endl;
...

int main()
{
	TheData DataIn;
	return 0;
}

Sorry if this is tedious stuff :D

Also I saw something about creating class objects that simply created the object straight after the class definition, e.g.

class TheData
{
public:
	void InputData();
	void DisplayMembers();
	TheData();
	~TheData();
protected:
	double* Values;
	int Length;
	bool Valid;
}	DataOut; // This little bit, does that create a class object called DataOut?

>>// Am I right to pass it just by its name? Will the constructor know its name?
No. Inside the body of a member function, the object itself is accessible via the "this" pointer. This is a pointer to the instance of the class that was attached to the call to the member function. To turn that pointer to a value or reference, you just need the dereferencing operator, as such:

TheData::TheData()
{
	Valid = false;
	Length = 0;
	Values = 0;
	TheMenu::MainMenu(*this); // *this dereferences the this pointer to make it a reference (or value).
}

>>// I can still use the same name right?
Yes. Because DataIn from main() is only visible in the main() function, there is no problem in reusing the same name as the parameter name. In fact, if it is a good name that describes the purpose of the variable, then it is preferable that you use that name for the parameter. But, it does not have to be the same name, it can be any name that you want and the name you choose in the definition of the function is the name that you need to use to use that parameter within that function.

>>// This little bit, does that create a class object called DataOut?
Yes. That creates a global object of class TheData with the name DataOut. Since it is global, it is accessible anywhere. This is ok, but considered bad programming practice (both the act of using global variables and the act of declaring a class and an object at the same time, because it is easy to miss when you read the code). So, that code is just a short-cut and is equivalent to:

class TheData
{
public:
	void InputData();
	void DisplayMembers();
	TheData();
	~TheData();
protected:
	double* Values;
	int Length;
	bool Valid;
};

TheData DataOut;

Ah ok, I sort of know about *, it's sort of the opposite of &, right? with & getting the address of a variable, and * getting the value at that address?

I shall hopefully have a working little program soon. Thanks again for your help, I should probably get some sleep now, hopefully a good sleep knowing that I can now finish this!

Thanks, Mike!

Think it was too much to hope for that I didn't run into any other problems.

Basically I'm unable to edit my protected members...

class TheData
{
public:
	void InputData();
	void DisplayMembers();
	void WriteMembers(int choice, bool tValid, int tLength, double* tValues); 
	void ReadMembers(int choice, bool& tValid, int& tLength, double*& tValues); 
	TheData();
	~TheData();
protected:
	double* Values;
	int Length;
	bool Valid;
};

void TheData::WriteMembers(int choice, bool tValid, int tLength, double* tValues)
{
	switch(choice) {
case '1':
	Valid = tValid;
	break;
case '2':
	Valid = tValid;
	Length = tLength;
	Values = tValues;
	break;
	}
	return;
}

// This is the call that I wanted to use to update the protected members.
DataIn.WriteMembers(2, tValid1, tLength1, tValues1);

So I run the code and it just won't change the members. If I put a cout in there I can see that the values I passed into the WriteMembers() function still contain the values I wanted, but the members don't update even after the assignments.

Something obvious I'm missing?

>>Something obvious I'm missing?
Yes. At line 19 and 22, in the cases, you use the characters '1' and '2'. These are not equal to the numbers 1 and 2. The characters '1' and '2' are ASCII characters corresponding to the actual numbers 49 and 50 (look-up "ascii table"). So, you should not use those single quote marks around the numbers and it should work fine.

Oh damn, something so tiny huh :P
I'm sure I've fell victim to that problem before, how annoying.

Sorry to keep picking your brain, but you seem to have all the answers :)

So I'm reading from a file...

FilePointer >> input;
length = int (input);
double *LoadData = new double [length];//works
for (int i = 0; i < length; i++) {
	FilePointer >> t;
	LoadData[i] = t;
	cout << LoadData[i] << " ";
}
double* MemLocation = new double [LoadData[count+1]];//doesn't work

No doubt it's something tiny. I had some error about cout being ambiguous for hours, turns out I was missing a } ...

Another thing, when saving data to files, e.g. data.txt. If the file already exists and has, for instance, 26 characters stored, if I were to save 14 characters, would the rest of the file be written with zeros, or would it have the remainder of the original file still there?

>>LoadData[count+1]
This is a variable of type double. You cannot use a double as the number of elements in an array, you can only have an integral number of elements (i.e. no fractional number of elements!). You need to either convert it to an integer or change the LoadData array to an integer array.

>>would the rest of the file be written with zeros, or would it have the remainder of the original file still there?

It depends. If you open the file with the flag std::ios::append, whatever you write in the file will be written at the end of whatever was there before. If you open the file without the append flag, then the entire content of the file will be deleted and it will start writing the new content from the start of an empty file. So, in your example, if the file has 26 chars and you write 14 chars. If the file was opened with "append", then the final file will have 40 chars. If the file was opened without "append", then the final file will have 14 chars only. In other words, the default behaviour is "overwrite" and the append flag lets you add content to the file.

Sorry if I've got this wrong, but are you say that it's the index of the array that's the problem? But my index of the array, count, is an int.
And the errors:

double* MemLocation = new double[LoadData[count+1]]; //'initializing' : conversion from 'double' to 'unsigned int', possible loss of data
Filter.WriteMembers(2, true, LoadData[count+1], MemLocation); //'argument' : conversion from 'double' to 'int', possible loss of data

Your LoadData is an array of double (floating-point values, not integral values) and so, picking an element of it with LoadData[count+1] gives a value that is of type double and you use that value as a size for the new array MemLocation, but a size has to be an integral value. So, the compiler might implicitly convert the double to an integral type (in this case unsigned int) but will also issue a warning because it usually does not make sense to convert a double to an int and it is often not how you would like the conversion to be done (it doesn't round-off the number, it just truncates all the decimals, even if the value is 1.999999, it still will convert to 1 as an integer). So, if you are always using this LoadData to store integral numbers, make it an array of integral types (int or unsigned int) but not an array of doubles.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.