In this tutorial you will learn the basics of C++ and how to convert constructors. This will allow you to write a simple C++ program that you will then compile. The program will be a Visual Studio console application
About this Tutorial –
Objectives –
This course is aimed at students who need to get up to speed in C++. The course introduces object-oriented concepts and shows how they are implemented in C++. The course does not require awareness or familiarity with object-oriented programming techniques, but programming experience would be useful but not necessarily required.
Audience
Students who are new to object orientation (or programming) and need to learn C++.
Prerequisites
No previous experience in C++ programming is required. But any experience you do have in programming will help. Also no experience in Visual Studio is required. But again any experience you do have with programming development environments will be a valuable.
Experience using a contemporary OO language such as Java or C# would be useful but is not required.
Contents
The C++ course covers these topics and more:
Introduction to C++: Key features of C++; Defining variables; Formulating expressions and statements; Built-in data types; Console input/output
Operators and types: Assignment; Compound Assignment; Increment and decrement operators; Const declarations; Type conversions
Going Further with Data Types: Enumerations; Arrays; Using the standard vector class; Using the standard string class; Structures
The class can actually point to a standard C++ string internally, to hold the text string actualString;
The class also needs a reference count (set to 1 initially) to keep count of how many RefCountedString objects point to the same C++ string object int refCount;
Add constructors to initialize a RefCountedString from a character literal (such as "hello") and from a standard C++ string // Default constructor, to hold brand new text (or no text).
RefCountedString::RefCountedString(const char * text)
{
// Create a new representation object, to hold brand new text (for the first time).
pRep = new RefCountedStringRep(text);
}
// Copy constructor, to share text with an existing RefCountedString.
RefCountedString::RefCountedString(const RefCountedString & other)
{
// Just use the same representation object as the other RefCountedString.
// This will just increment an internal ref count, rather than doing any actual copying.
HookExistingRep(other.pRep);
}
// Assignment operator, to share text with a different existing RefCountedString.
RefCountedString & RefCountedString::operator = (const RefCountedString & other)
{
// Guard against assignment-to-self.
if (this != &other)
{
// Unkook from existing representation object (this will decrement its ref count).
UnhookExistingRep();
// Then hook onto a different existing representation object (this will increment that one's ref count).
HookExistingRep(other.pRep);
}
return *this;
}
// Destructor.
RefCountedString::~RefCountedString()
{
// Just unhook ourself from our representation object.
UnhookExistingRep();
}
In main(), create a RefCountedString object and test it behaves OK so far // Create a brand-new RefCountedString.
RefCountedString fruit1("apple");
cout << "Fruit1: " << fruit1 << endl;
// Create another RefCountedString from an existing one. This calls the copy constructor.
RefCountedString fruit2(fruit1);
cout << "Fruit2: " << fruit2 << endl;
// Create yet another RefCountedString from an existing one. This also calls the copy constructor.
RefCountedString fruit3 = fruit2;
cout << "Fruit3: " << fruit3 << endl << endl;
In C++, the word "copying" has a very specific meaning
"Initialize a new object from another object of the same type"
Examples
Imagine we already have these objects: date christmas(25, 12, 2012);
date boxingDay(26, 12, 2012);
... and this function (takes a date by value, not by ref!): void myPrintDate(date theDate) { ... }
Then copying occurs in these situations: date familyDay(christmas); // Copies "christmas" object into "familyDay".
date footballDay = boxingDay; // Copies "boxingDay" object into "footballDay".
myPrintDate(footballDay); // Copies "footballDay" object into func param
Default Copying
By default, all classes have a default copy constructor
Takes an instance of the same type (by const-ref)...
The default constructor for the date class is equivalent to the following code
Note, you wouldn't bother actually writing this yourself, the compiler would generate it anyway class date
{
private:
int d, m, y;
public:
date(const date & other);
...
};
// Date
date::date(const date & other)
: d(other.d),
m(other.m),
y(other.y)
{}
Problems with Default Copying
The default copy constructor doesn't work properly for some classes
In our string class, the default copy constructor won't work
It just copies pointers, rather than copying the underlying data class string
{
private:
char * text;
public:
string(const string & other);
...
};
// String
string::string(const string & other)
: text(other.text)
{}
Defining a Copy Constructor
For classes like our string class, we have to define a "proper" copy constructor
Otherwise the class doesn't work properly
Example:
Here's a copy constructor for our string class: class string
{
private:
char * text;
public:
string(const string & other);
...
};
// Copy for String
string::string(const string & other)
{
int len = strlen(other.text);
text = new char[len + 1];
strcpy(text, other.text);
}
Lab
Implementing some simple operators
Add some operators to the class, such as the following:
"Assign an existing object (e.g. from another object of same type)"
Note the difference between copying and assignment:
Copying: initialization of a new object
Assignment: re-assignment of an existing object
Examples:
Imagine we already have these objects: date date1(19, 1, 2012);
date date2(3, 12, 2012);
Now consider the following: date2 = date1; // Reassigns date2, with the value of date1.
Default Assignment
By default, all classes have a default assignment operator
Takes an instance of the same type (by const-ref)...
Performs member-wise assignment
Returns itself (by reference)
Example:
The default assignment operator for a date class is equivalent to the following code
Note, you wouldn't bother actually writing this yourself class date
{
private:
int d, m, y;
public:
date & operator=(const date & other);
...
};
//
date & date::operator=(const date & other)
{
d = other.d;
m = other.m;
y = other.y;
return *this;
}
Problems with Default Assignment
The default assignment operatordoesn't work properly for some classes
In our string class, the default assignment operator won't work
It just reassigns pointers, rather than copying the underlying data (and deleting the old data) class string
{
private:
char * text;
public:
string & operator=(const string & other);
...
};
//
string & string::operator=(const string & other)
{
text = other.text;
return *this;
}
Defining an Assignment Operator
For classes like our string class, we have to define a "proper" assignment operator
Otherwise the class doesn't work properly
Example:
Here's an assignment operator for our string class: class string
{
private:
char * text;
public:
string & operator=(const string & other);
...
};
// Assignment
string & string::operator=(const string & other)
{
// Delete our old text.
delete [] text;
// Allocate memory for new text.
int len = strlen(other.text);
text = new char[len + 1];
// Copy in new text.
strcpy(text, other.text);
// Finally, return reference to ourself.
return *this;
}
Lab
Implementing copy-on-write semantics
Add some methods and operators that would modify a RefCountedString, such as the following:
str.ToUpperCase() // Convert our string to uppercase.
RefCountedString & RefCountedString::ToUpperCase()
{
// Make sure we have a unique representation object.
EnsureUniqueRep();
// Convert our text to uppercase.
strupr(const_cast(pRep->actualString.c_str()));
return *this;
}
str.ToLowerCase() // Convert our string to lowercase.
RefCountedString & RefCountedString::ToLowerCase()
{
// Make sure we have a unique representation object.
EnsureUniqueRep();
// Convert our text to lowercase.
strlwr(const_cast(pRep->actualString.c_str()));
return *this;
}
str1.SetAt(pos, 'A') // Set character at specified index position.
RefCountedString & RefCountedString::SetAt(int index, char ch)
{
// Make sure we have a unique representation object, which we can modify with impunity :-)
EnsureUniqueRep();
// Set character at specified position.
pRep->actualString[index] = ch;
return *this;
}
str1 = str2 bool RefCountedString::Equals(const RefCountedString & other) const
{
// The 1st test for equality is whether the RefCountedString objects share the same representation object.
if (pRep == other.pRep)
return true;
// The 2nd test for equality is whether the RefCountedString objects happen to contain equal string objects.
if (pRep->actualString == other.pRep->actualString)
return true;
// If we get here, the RefCountedString objects contain unequal string objects.
return false;
}
Think carefully about these methods... when you modify one RefCountedString object, it should NOT modify any other RefCountedString objects that happen to have the same text at that moment. In other words, you need "copy-on-write" semantics (i.e. when a RefCountedString is modified, if the reference count is > 1, then you need to break off a separate copy that you can modify in isolation from all the other shared references)
You'll also need to implement a copy constructor and assignment operator with these sentiments in mind.
In main(), write some code to test your class thoroughly // Change fruit1. This should snap off a separate representation object.
fruit1.ToUpperCase();
cout << "After modifying fruit1... " << endl;
cout << "Fruit1: " << fruit1 << endl;
cout << "Fruit2: " << fruit2 << endl;
cout << "Fruit3: " << fruit3 << endl << endl;
// Let's see what's equal.
cout << "fruit1==fruit2? " << (fruit1 == fruit2) << endl;
cout << "fruit2==fruit3? " << (fruit2 == fruit3) << endl;
cout << "fruit1==fruit3? " << (fruit1 == fruit3) << endl << endl;
Etc...
If you liked this post, please comment with your suggestions to help others.
If you would like to see more content like this in the future, please fill-in our quick survey.
Manage cookie consent
You can view this website without consenting to extra cookies. However you will need to accept the 'marketing' cookies to send messages via the contact forms & see any maps displayed on the site
Functional
Always active
Cookies necessary for the website to work.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.Cookies used to track user interaction on the site, which helps us to evaluate & improve the website.
Marketing: Forms & additional content (Marketing)
We need your permission to place ‘marketing’ cookies, so you are able to use the contact forms & see embedded content e.g. videos and maps. - - - - We have added protection to our contact forms to help prove that a human (rather than a spambot) is filling
If you would like to see more content like this in the future, please fill-in our quick survey.