Scope Scope is such an important thing that we ll review what we know about scope now: Local (block) scope: A name declared within a block is accessible only within that block and blocks enclosed by it, and only after the point of declaration. The names of formal arguments to a function in the scope of the outermost block of the function have local scope, as if they had been declared inside the block enclosing the function body. File scope: Any name declared outside all blocks or classes has file scope. It is accessible anywhere in the translation unit (file) after its declaration. Names with file scope that do not declare static objects are often called global names. Function scope: Labels are the only names that have function scope. They can be used anywhere within a function but are not accessible outside that function. Prototype scope: Names declared in a function prototype are visible only until the end of the prototype. 1
Class Scope More about class scope now: data members and function members have class scope this means that they can be accessed within the class s methods by name outside a class s scope, class members cannot be referenced by name but the public members can be referenced by a handle an object name a reference to an object (remember this is an alias) a pointer or tracking handle to an object The point is that the system has to know WHICH day s weather you are talking about, for example Practicalities: access through an object name or through a reference use the dot (.) access operator access through a pointer or tracking handle to the object uses the arrow (->) access operator Some variables (those defined within a method) have only method scope 2
Class Scope - Examples remember that names of class members have class scope. Class member functions can be accessed only by using the member-selection operators (. or ->) or pointer-to-member operators (.* or ->*) on an object or pointer to an object of that class weather today = *(new weather()); weather *monday; double degday; today.settemp (22, 12); monday = &today; degday = monday -> degreedays(); only legal if the assignment operator is defined for weather objects!! all nonstatic class member data is considered local to the object of that class. 3
Making Instances of Classes (Objects) We ve seen that objects can be made: When an object of a particular class is declared When the operator new is used to make one In either case, the function that actually initializes the object is a constructor for the class Constructors initialize the instance variables of objects There could be multiple constructors for a class - overloaded constructors can be used to provide different ways to initialize objects of a class Even if the constructor does not do so explicitly, all data members are initialized Primitive numeric types are set to 0 Boolean types are set to false Pointers are set to null If you don t define a constructor for a class, a default constructor is provided It has no code and takes no parameters there can only be one default constructor (i.e. one with no arguments) for each class 4
weather::weather(int a, int b) {settemps (a, b); Remember the class weather class weather { public: weather(int, int); weather(); void settemps (int, int); double avgtemp (); double degreedays (); void printdata (); weather::weather() { settemps (0, 0); void weather::settemps (int max, int min) { maxtemp = (max >= min? max : min); private: mintemp = (min <= max? min : max); int maxtemp; int mintemp; double weather::avgtemp () ; { return ((maxtemp - static_cast <double> (mintemp))/2.0 + mintemp); double weather::degreedays () { return (65 - ((maxtemp - static_cast <double>(mintemp))/2.0 + mintemp)); void weather::printdata() { cout << "\n the max temp is " << maxtemp << " and the min temp is " << mintemp << endl; 5
Understanding What Happens if Suppose that we re dealing with the weather class and we have the following code: weather d1, d2; d1.settemps(21, 0); d2 = d1; What happens here? The declaration of d1 and d2 causes space for both of these variables to be allocated and also calls the default constructor to give the data members default values. The system has built an overloaded assignment operator for you it does component-wise assignment (and it works just fine because the members are int type things) 6
What if we have data elements that are pointers or tracking handles (for managed stuff)? Consider the following scenario: ref class MemberInfo { public: MemberInfo(); void entername(); void enterage(); void display(); private: String ^name; //notice the dynamic member int age; ; The dynamic member is a String that is a managed type and the class is a reference class!! (note the ref in the definition)! 7
Default Constructor for the class MemberInfo We may as well define a default constructor for this class, and this one will do: MemberInfo::MemberInfo(){ name = "no name"; age = 0; ref class MemberInfo { public: MemberInfo(); void entername(); void enterage(); void display(); private: String ^name; int age; ; 8
And member function definitions: void MemberInfo::enterName(){ Console::Write( Enter member s name: ); this -> name = Console::ReadLine(); void MemberInfo::enterAge(){ Console::Write( Enter member s age: ); this -> age = Int32::Parse(Console::ReadLine()); ref class MemberInfo { public: MemberInfo(); void entername(); void enterage(); void display(); private: String ^name; int age; ; Notice the use of the this pointer! void MemberInfo::display(){ Console::WriteLine( Member s name is: {0 and age is {1, this -> name, this -> age.tostring()); Every object has access to a pointer to itself called the this pointer that can be used to implicitly reference the data members and methods of an object 9 from the methods of that object
Can we do a better default constructor? Instead of MemberInfo::MemberInfo(){ name = "no name"; age = 0; Could we do this? MemberInfo::MemberInfo(){ entername(); enterage(); 10
A driving function using MemberInfo: int main() { MemberInfo ^n1, ^n2; //declare two tracking handles n1 = gcnew(memberinfo); //make a new object n1 -> entername(); n1 -> enterage(); n1 -> display(); n2 = n1; n2 -> display(); Console::ReadLine(); return 0; This works just fine!! 11
But if we change it like this: int main() { MemberInfo ^n1, n2; n1 = gcnew(memberinfo); n1 -> getname(); n1 -> getage(); n1 -> display(); n2 = *n1; n2.display(); return 0; //declare a tracking pointer and a //variable //make a new object We ll get an error: operator = is unavailable in MemberInfo The reason for this is that the assignment operator has no automatic override due to the dynamic data! 12
Recap: the class MemberInfo (expanded a bit) #pragma once #ifndef MEMB1 #define MEMB1 using namespace System; Class Definition Goes into the header file ref class MemberInfo{ public: MemberInfo();// default constructor void entername();// method for entering name void enterage();// method for entering age String^ getname();// method for extracting name int getage();// method for extracting age void setname(string^);// method for setting name to a value void setage(int);// method for setting age to a value void display();// method for display of the object s data private: String ^name; int age; static int count; ; #endif 13
#include "stdafx.h #include <iostream> #include "MemberInfo.h" using namespace System; using namespace std; // the default constructor MemberInfo::MemberInfo(){ name = "no name"; age = 0; count++; // set name to a value indicated in operand void MemberInfo::setName (String^ s){ name = s; // set age to a value indicated in operand void MemberInfo::setAge (int a){ age = a; // accept name entered by user void MemberInfo::enterName(){ Console::WriteLine("Enter the member's name: "); this->name = Console::ReadLine(); //name = Console::ReadLine(); Method definitions for the class Goes in the.cpp file // accept age entered by user void MemberInfo::enterAge(){ Console::Write ("Enter the member's age: "); this->age = Int32::Parse(Console::ReadLine()); // return value of the name data member String ^ MemberInfo::getName(){ return name; // return value of the age data member int MemberInfo::getAge(){ return age; // display the object s data members void MemberInfo::display(){ Console::WriteLine("Member Name: {0 and age = {1", name, age.tostring()); Console::WriteLine("Number of Members: {0", count.tostring()); 14
Classes with Data Members that are Pointers In native C++, when a data member is a pointer, the constructor must allocate space for it and a destructor must deallocate space (otherwise we get memory leaks). Similarly when the class is managed the constructor has to allocate but the gc cleans up. Whenever a class has at least one data member that is a pointer, you MUST write a destructor a copy constructor an overloaded assignment operator Otherwise you will get error messages 15
Destructors A destructor is a function that: Is automatically invoked when an object is destroyed i.e. at the end of the block in which an object was declared Performs termination housekeeping In particular, it recovers memory to be reused Takes no arguments and returns no value Has the same name as the class preceded by a ~ There is only one destructor for a class no overloading is possible The system automatically generates a destructor for a class, but Programmers do have to write a destructor when their class dynamically allocates memory (which can happen when data members are pointers) Even if the class is a managed class, a destructor should be written! 16
Classes with Data Members that are Pointers Now to the concept of a copy constructor: There are certain situations when a copy of an object must be made: when passing the object by value when returning an object Furthermore, we want to be able to initialize an object by another object. Creating a copy of an object is the job of the copy constructor. This is a special kind of constructor that takes as argument an object of the same type as the class. So why do you have to write one when a data member is a pointer? 17
First, why do we need a copy constructor? If a copy constructor is missing (for any standard C++ class), the compiler creates a default copy constructor that performs member-by-member copying. If we tried to consider a default for the MemberInfo class, the default might look like: MemberInfo (const MemberInfo& init) { name = init.name; age = init.age But this can t work let s see why suppose we trace the activity in the following: MemberInfo ^president = gcnew MemberInfo ( Obama, 44); MemberInfo clone(^president); delete [] president; president = NULL; 18
Trace step 1: MemberInfo ^president = gcnew MemberInfo ( Obama, 44); president 44 O B A M A 19
Trace step 2: MemberInfo clone(^president); Let's look inside the copy constructor: name = init.name; age = init.age president O B A M A 44 clone 44 We need a way to make the two objects be independent objects! Let s see why -- 20
Trace step 3: (for the managed case) President = NULL; 44 president OBAMA clone 44 Managed datatypes are automatically garbage collected this does not happen in standard C++!! that means that the delete[] president; was not needed! 21
Trace step 3: (native code unmanaged) Delete [] president; president = NULL; In standard C++ (using a standard C string for the name) you d wind up with this scenario on deletion of president. president clone 44 Freed space The two objects really need to be independent objects, so we need to provide a copy constructor can t use the default! MemberInfo::MemberInfo ( MemberInfo %s) {String^ str = s.name; name = str; age = s.age; 22
This is related to Assignment the = Operator: What Happens by Default Ordinarily, the compiler will generate a default assignment operator a point class (that has two double data members) is an example of a case in which the = operator is automatically overloaded by the compiler If you define any assignment operator that takes the class as a parameter, the compiler cannot generate a default assignment operator for that class. In these cases, you have to provide an overloaded assignment operator 23
Class with Very Simple Destructor, Copy Constructor and Operator = overloaded Suppose that we have a class point with header: class point { ; public: point (); point (double x, double y ); void setpoint(double, double); double get_x (); double get_y (); ~point(); // default constructor // overloaded constructor // sets the point s value // destructor point (point &p); // copy constructor point & operator = (point &p); // overloading of = private: double x; double y; 24 pointclass example
Class with Very Simple Destructor, Copy Constructor and Operator = overloaded Suppose that we have a class point with header: class point { ; public: point (); point (double x, double y ); void setpoint(double, double); double get_x (); double get_y (); ~point(); // default constructor // overloaded constructor // sets the point s value // destructor point (point &p); // copy constructor point & operator = (point &p); // overloading of = private: double x; double y; 25 pointclass example
Constructor and Destructor Methods for class point point::point(){ cout << "default constructor \n"; x = 0.0; y = 0.0; //default constructor point::point (double xval, double yval) // overloaded constructor { cout << "overloaded constructor with values " << xval << ", " << yval << endl; setpoint (xval, yval); point::~point(){ // destructor - no body needed here cout << "destructor \n"; // destructor 26
Mutator and Inspector methods for class point // mutator method void point::setpoint(double xval, double yval){ cout << "setpoint with values " << xval << ", " << yval << endl; x = xval; y = yval; // accessor methods double point::get_x(){ return x; double point::get_y(){ return y; 27
Copy Constructor, and Overloaded = operator point::point (point &p){ // copy constructor double xval = p.get_x(); double yval = p.get_y(); cout << "copy constructor with " << xval << ", " << yval << endl; setpoint (xval, yval); point & point::operator = (point &p){ // overloading of = cout << "overloaded = operator" << endl; double x = p.get_x(); double y = p.get_y(); setpoint(x, y); return *this; 28
With driving function: int main() { point p1, p2; point p3(1.5, 25.5); p1.setpoint(2.2, 3.3); p2 = p1; point p4(p3); return 0; Notice that these are declarations of objects of type point 1. default constructor called to create p1 2. default constructor called to create p2 3. overloaded constructor called to create p3 4. setpoint method called to set new values for p1 5. overloaded assignment operator called to set a new value for p2 6. copy constructor called to create a copy of p3 in p4 29
With driving function: int main() { point p1, p2; point p3(1.5, 25.5); p1.setpoint(2.2, 3.3); p2 = p1; point p4(p3); return 0; 30