Programming C, C++, Java, PHP, Ruby, Turing, VB
Computer Science Canada 
Programming C, C++, Java, PHP, Ruby, Turing, VB  

Username:   Password: 
 RegisterRegister   
 WIP - C++ OOP Whirlwind
Index -> Programming, C++ -> C++ Tutorials
View previous topic Printable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
wtd




PostPosted: Thu Oct 26, 2006 5:25 pm   Post subject: WIP - C++ OOP Whirlwind

Let's create a class!

code:
class Foo
{

};


This class does precisely nothing useful. However, what it does do is create a new type. We can declare a variable of this type.

code:
Foo f;


We can dynamically allocate memory for that type.

code:
Foo *f = new Foo;


We can delete it.

code:
delete f;


We can write a function which takes such a value as a parameter.

code:
int bar(Foo f)
{
   return 42;
}


We can use that type in overloading existing functions.

code:
std::ostream& operator<<(std::ostream& out, const Foo& f)
{
   return out << 42;
}


Member functions!

code:
class Foo
{
   int bar();
};


So now we can just create a Foo object and call that method, right?

code:
Foo f;
std::cout << f.bar() << std::endl;


Not so fast. By default, everything in a class is private. We'll have to explicitly make that member fucntion public.

code:
class Foo
{
   public:
      int bar();
};


Now the code we'd written previously works. Or, at least it will if we actually define the member function we declared. Otherwise, you'll get a linker error when the linker goes looking for the member function that isn't actually defined.

code:
int Foo::bar()
{
   return 27;
}


Now, let's try calling that again.

code:
Foo *f;
std::cout << f->bar() << std::endl;


Oops! The variable "f" hasn't yet been initialized.

code:
Foo *f = new Foo;
std::cout << f->bar() << std::endl;


Dodged a bullet there. No one likes dealing with segmentation faults.

Only one problem remaining.

code:
const Foo f;
std::cout << f.bar() << std::endl;


Now, we have a constant Foo object. Really, this makes little sense, as at this point all Foo objects are basically constant. However, this is a big C++ quirk, and one worth understanding.

To make bar work in this case, we'd have to qualify it as being ok to call on a const object.

code:
class Foo
{
   int bar() const;
};

int Foo::bar() const
{
   return 27;
}


State

code:
class Foo
{
   int _bar;
   
   public:
      int bar() const;
};

int Foo::bar() const
{
   return _bar;
}


So far all Foo objects have been boring because they're all exactly the same. To differentiate them, each object needs to hold some internal state that can be different from other Foo objects.

Now, in the above code sample, each Foo object will now have a member variable called "_bar."

There's just one problem. Since everything in a class is private unless otherwise specified, there's no way to set the value of that variable.

Construction

code:
class Foo
{
   private:
      int _bar;
   
   public:
      Foo(int bar);
   
      int bar() const;
};

Foo::Foo(int bar) : _bar(bar)
{
}

int Foo::bar() const
{
   return _bar;
}


Constructors provide a way to initialize the state of an object.

We may now write the following.

code:
Foo f = Foo(31);
std::cout << f.bar() << std::endl;


And the output will be as follows.

code:
31


We may have more than one constructor, following C++ function overloading rules. Though constructors are not actually functions.

code:
class Foo
{
   private:
      int _bar;
   
   public:
      Foo();
      Foo(int bar);
   
      int bar() const;
};

Foo::Foo() : _bar(0)
{
}

Foo::Foo(int bar) : _bar(bar)
{
}

int Foo::bar() const
{
   return _bar;
}


Here we've created a default constructor. Such a constructor will be called in either of the following situations.

code:
Foo f1;
Foo *f2 = new Foo;


It's important to note that we might also accomplish the above effect using default parameter values.

code:
class Foo
{
   private:
      int _bar;
   
   public:
      Foo(int bar = 0);
   
      int bar() const;
};

Foo::Foo(int bar) : _bar(bar)
{
}

int Foo::bar() const
{
   return _bar;
}


However, we cannot achieve the following effect using default values.

code:
class Foo
{
   private:
      int _bar;
   
   public:
      Foo(int bar = 0);
          Foo(const Foo& other);
   
      int bar() const;
};

Foo::Foo(int bar) : _bar(bar)
{
}

Foo::Foo(const Foo& other) : _bar(other._bar)
{
}

int Foo::bar() const
{
   return _bar;
}


This is known as a copy constructor. It literally copies the state of another object into the new object.

Being explicit

code:
class Foo
{
   private:
      int _bar;
   
   public:
      explicit Foo(int bar = 0);
          explicit Foo(const Foo& other);
   
      int bar() const;
};

Foo::Foo(int bar) : _bar(bar)
{
}

Foo::Foo(const Foo& other) : _bar(other._bar)
{
}

int Foo::bar() const
{
   return _bar;
}


Previously our constructors have not been qualified as explicit. Without this, we could have had code of the sort that follows.

code:
Foo f = 42;
std::cout << f.bar() << std::endl;


With the explicit qualifier, we now require one of the following.

code:
Foo f = Foo(42);
Foo f(42);


But this also makes it impossible to write the following.

code:
Foo f = Foo(42);
Foo g = f;


Instead we would have to:

code:
Foo f = Foo(42);
Foo g = Foo(f);


Instead, perhaps we should :

code:
class Foo
{
   private:
      int _bar;
   
   public:
      explicit Foo(int bar = 0);
          Foo(const Foo& other);
   
      int bar() const;
};


Assignment

code:
class Foo
{
   private:
      int _bar;
   
   public:
      explicit Foo(int bar = 0);
          Foo(const Foo& other);
          
          Foo& operator=(const Foo& other);
   
      int bar() const;
};

Foo::Foo(int bar) : _bar(bar)
{
}

Foo::Foo(const Foo& other) : _bar(other._bar)
{
}

Foo& Foo::operator=(const Foo& other)
{
   _bar = other._bar;
   return *this;
}

int Foo::bar() const
{
   return _bar;
}


Previously we showed off assignment-style construction by leaving the copy constructor non-explicit. This only applies to construction. Once the object is constructed, other requirements come into play. Then we need to overload the assignment operator, as has been done above.

Since this changes the state of the object, it is naturally not qualified by "const."

Another operator

code:
class Foo
{
   private:
      int _bar;
   
   public:
      explicit Foo(int bar = 0);
          Foo(const Foo& other);
          
          Foo& operator=(const Foo& other);
   
      int bar() const;
          
          bool operator==(const Foo& other) const;
          bool operator<(const Foo& other) const;
};

Foo::Foo(int bar) : _bar(bar)
{
}

Foo::Foo(const Foo& other) : _bar(other._bar)
{
}

Foo& Foo::operator=(const Foo& other)
{
   _bar = other._bar;
   return *this;
}

int Foo::bar() const
{
   return _bar;
}

bool Foo::operator==(const Foo& other) const
{
   return _bar == other._bar;
}

bool Foo::operator<(const Foo& other) const
{
   return _bar < other._bar;
}


It's quite common, and highly useful to overload various operator for objects. A few of the more common ones are the equality and the less than operator. This permits various functions in the standard library to determine proper ordering of collections of objects.
Sponsor
Sponsor
Sponsor
sponsor
Display posts from previous:   
   Index -> Programming, C++ -> C++ Tutorials
View previous topic Tell A FriendPrintable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

Page 1 of 1  [ 1 Posts ]
Jump to:   


Style:  
Search: