COMP345:
Advanced Program Design with C++
Lecture 2
C++ Fundamentals
Department of Computer Science and Software Engineering Concordia University
Contents
Data types
Variable declaration and initialization Type checking
Type coercion
Pointers
Strings
Data types
Highly similar to Java data types
Basic types are not classes (like Java)
Pit trap: different compilers will have different ranges for most basic data types Some programs potentially will behave differently across different platforms Hence, lack of portability of C++ programs
User-defined data types using struct (as in C), as well as class (object-oriented programming)
Both are allowed in the same program
In fact, they are almost equivalent, but struct was kept for backward compatibility A struct can have data members, methods, constructors, destructors, etc
One difference is that a struct sets its members as public by default
Data types: simple types size, range and precision
Data types: simple types size, range and precision
Data types: literals
Literals
2, 5.75, Z, Hello World
Considered constants: cant change in program
All literals have an inherent type that can be determined during lexical analysis
Like many other languages, C++ uses escape sequences for string literals:
Contents
Data types
Variable declaration and initialization Type checking
Type coercion
Pointers
Strings
Variable declaration
A variable can be declared for any type valid in the current scope.
int x; double y; myClass mc;
Multiple variables of the same type can be declared on the same declaration: int x,y,z;
Any declared name can be referred to thereafter in the scope in which it is declared.
Variable initialization
Declarations can include an optional initialization, which can use different syntactical forms:
Type a1 {v}; Type a2 = {v}; Type a3 = v; Type a4(v);
All of these are widely used and apparently equivalent. However:
Some are restricted to use in certain situations.
Only the first one is universally usable, and is actually safer, as it implicitly does
some checking of the value passed versus the specified type.
int a1 = 1.5; //allowed using truncation
int a1 {1.5}; //not allowed, as type checking is enforced
Variable initialization
In C++, initialization is a general concept whereby variables are created with an initial value supplied at creation.
This is as opposed to the creation of variables without an initial value, which implies that a memory space is assigned to a variable, but the previously assigned content of the memory space is used.
Initialization is thus safer, but is slightly time-consumptive.
Initialization may seem a simple concept, but in C++ there is a proliferation of rules and special
cases related to initialization.
Rules vary widely across different C++ standards.
Even though you may think that you use simple initialization, expect to be faced with special/obscure cases.
The following slides briefly describe the different kinds of initializations and some simple examples.
Variable initialization
Default initialization: variable is constructed with no initializer.
For basic types, the previous value held in the memory space is kept.
For objects, the default constructor is called.
int x;
std::string s;
classA *objAv1 = new classA;
Value initialization: variable is constructed with an empty initializer. For basic types, the value is zero-initialized.
For objects, each member is value-initialized.
int x{};
std::string s{};
classA objA = classA();
Variable initialization
Direct initialization: variable is constructed using explicit constructor arguments. For basic types, no constructors, but constructor call syntax can be used.
For objects, the corresponding constructor is called
int x(4);
std::string s(hello);
classA objA(value1,value2,value3);
Copy initialization: Variable is created using the value(s) from an existing object of the same type, or uses a converting sequence if available. Applies only to named objects.
std::string s = hello;
classA objAv1; classA objAv2(objAv1);
classA objAv3 = objAv1;
Copy initialization is implicitly used when passing and returning objects by value.
Variable initialization
List initialization: Initializes an object from a braced initialization list.
std::string s{a, b, c};
int n1{1}; // direct-list-initialization int n2 = {1}; // copy-list-initialization
Reference initialization: Binds a reference to an object. char& c = a[0]
int i1; int& iref = &i1
Reference initialization is used implicitly when a value is passed by reference or a reference is returned from a function.
Variable initialization
Aggregate initialization: Initializes an aggregate from braced initialization list. It is list initialization but applied to aggregates.
Simply stated, an aggregate is either: an array
an object of a class that has only public members and no constructors Definition of an aggregate varies between standards.
char a[3] = {a, b};
int i[3] = {1,2,3};
class aggrA{
int a, b, c; aggrB b;
class aggrB{
int x,y; }
}
aggrA a1 = {1,2,3,{4,5}};
Contents
Data types
Variable declaration and initialization Type checking
Type coercion
Pointers
Strings
Type checking, type coercion, type conversion
C++ uses a manifest typing strategy
Variables and values are assigned types explicitly in the source code
Values can only be assigned to variables declared as having the same type
However, C++ allows type coercion, i.e. implicitly or explicitly changing the type of variables or values
This loophole, among other things, makes C++ a weakly typed language Type mismatches
General Rule: Cannot place value of one type into variable of another type
int var = 2.99; // 2 is assigned to var!
Only the integer part fits, so thats all that goes
Called implicit type casting or automatic type conversion When using pointers or classes, much more problematic!
Explicit type casting
C++ provides operators for explicit type coercion, or type casting static_cast
Explicitly casts intVar to double type
doubleVar = static_cast
Casting forces double-precision division to take place among two integer variables.
Equivalent in meaning to the following C syntax, even though the C++ cast operation is checked at compile time and is thus less prone to runtime errors
doubleVar = (double)intVar1/intVar2;
Explicit type casting
Different kinds of explicit type casting operations: static_cast
General-purpose type casting
const_cast
Cast-out constantness
dynamic_cast
Runtime-checked conversion of pointers and references within a single class hierarchy. Used for downcasting from a superclass to a subclass
reinterpret_cast
Implementation-dependent casting, performs a binary copy and assigns the new type to the resulting binary copied value. Highly unsafe and error-prone.
Inheritance: upcasting and downcasting
When dealing with classes and subclasses, one can declare objects of a supertype and manipulate them as one of its subclasses
void displayGeometricObject(GeometricObject& g) {
cout << “The radius is ” << g.getRadius() << endl;cout << “The diameter is ” << g.getDiameter() << endl; cout << “The width is ” << g.getWidth() << endl;cout << “The height is ” << g.getHeight() << endl;cout << “The area is ” << g.getArea() << endl;cout << “The perimeter is ” << g.getPerimeter() << endl;} Problem: subclass members are undefined in superclass GeometricObject ———————- areaperimeterCircle ————radius diameterRectangle ————- width height Inheritance: upcasting and downcasting May want to use static_cast:void displayGeometricObject(GeometricObject& g) {}GeometricObject* p = &g cout << “The radius is ” cout << “The diameter is ” cout << “The width is ” cout << “The height is ” cout << “The area is ” cout << “The perimeter is “<< static_cast
“;exit(1); } Most contemporary C++ compilers (C++98 and after) : new throws exception bad_alloc try {int * myarray= new int[1000]; } catch (bad_alloc&) {cout << “Error allocating memory.” << endl; } Pointers To deallocate dynamic memory, use the delete operator When value no longer needed Returns memory to freestore Example:int *p;p = new int(5);… //Some processing… delete p;p = NULL;//allocate memory//deallocate memory//prevents dangling pointer errors Deallocates dynamic memory “pointed to by pointer p ” p is then a dangling pointer that still points to its previously allocated value. If not deleted before the variable goes out of scope, memory is not freed, which creates a memory leak. Plus, dereferencing a dangling pointer leads to unpredictable results, ranging from getting a seemingly random value to a program crash. Managing dangling pointers and deallocating dynamically allocated memory is a very important aspect of proper C++ programming. Pointer arithmetic Can perform arithmetic operations on pointers Used to navigate arrays (covered later) Example:int *d;d = new int[10]; d refers to: address of new int[10] d + 1 refers to: address of new int[10] + 1*sizeof(int) d + 2 refers to: address of new int[10] + 2*sizeof(int) d[i] == *(&d[0]+i) == *(d+i) Pointers and const When using pointers, there are two separate meanings/usages of const: Specify the constantness of the pointer Specify the constantness of the value pointed to.int x;int * p1 = &x // non-const pointer to non-const int const int * p2 = &x // non-const pointer to const int int * const p3 = &x // const pointer to non-const int const int * const p4 = &x// const pointer to const int void, wild, dangling, and null pointersvoid pointer: a pointer that is allowed to be pointing to a value of any type. void increase (void* data, int psize){ if ( psize == sizeof(char) ){ char* pchar; pchar=(char*)data; ++(*pchar); } else if (psize == sizeof(int) ){ int* pint; pint=(int*)data; ++(*pint); }}int main (){char a = ‘x’;int b = 1602;increase (&a,sizeof(a)); increase (&b,sizeof(b));cout << a << “, ” << b << ‘
‘; return 0;}Disadvantage: need to cast them specifically in order to use them.void, wild, dangling, and null pointers wild pointer: a pointer that points to an arbitrary memory location. This is most often due to an uninitialized pointer declarationint *x;Here, x points to the address that corresponds to whatever value that already was in the memory space allocated to x.Dereferencing a wild pointer may lead to: Segmentation fault: pointing to an address that is not accessible by the programs process pointing to an address that contains read-only data Arbitrary value: pointing to a valid address that contains a valid but arbitrary integer valuevoid, wild, dangling, and null pointers dangling pointer: a pointer that used to point to a valid memory area, that has now been potentially reassigned to another usage.int* func(){int num = 1234; // num is local to func /* … */return # // func returns pointer to num} void func(){ClassA *objA = new ClassA(); /* … */delete(objA);}// *objA is deallocated.// objA is now a dangling pointer.// Dereferencing will still appear// to work, until the memory is used// for something else… void, wild, dangling, and null pointers null pointer: a pointer that points nowhere. int* x = nullptr; int* y = NULL; int* z = 0;Dereferencing a null pointer is a compilation error : safer pointer usage.Pointers should be initialized as null pointers. Dangling pointers should be assigned to null.Check for null pointer is then a good way to know if a pointer is valid or not.References Pointers are very powerful, as they allow: A variable to refer a value held by another variable. A variable to refer to different values held by different variables in time. Pass information around without having to copy it. However, due to their power, pointers bring additional complexities: Must use a special syntax (*, &, ->)
Possibility of dangling pointers, wild pointers, null pointers.
Pointers are unsafe, as their use may easily result in undefined behavior.
References are pointer variables that eliminate some of the disadvantages of pointers, at the cost of
eliminating some of their power.
Pointer arithmetic cannot be applied to a reference.
Any operation applied to a reference is actually applied onto the variable it refers to, including assignment.
Hence, references must be initialized upon declaration and cannot be changed afterwards.
Furthermore, given a reference int& r {v1}, &r returns a pointer to the object referred to by r.
Thus, we cannot even have a pointer to a reference.
References
A reference is in fact an alias for a memory space.
Terminology: the reference is an alias to a referent (the value pointed to). Both the reference and the referee represent the same value/object.
References
References are often used to pass parameters:
References
Or even return a value.
In combination, passing a reference and returning a reference allows to pass an object, process it, then return it, allowing the returned reference to be acted upon.
Smart pointers
Pointers are a very good example of a powerful C++ feature that is dangerous to use.
Very powerful and lean tool to use. But leads to:
Memory leaks: if a pointer is declared in a function and not deleted, the value that is pointed to is not freed. Dangling pointers: if a pointer is deleted, the pointer still points to the freed memory block.
Solution: use smart pointers
Reduce bugs
Retain similar efficiency and syntax
Control memory management by methods
auto_ptr (now deprecated), unique_ptr, shared_ptr, weak_ptr Defined in the
Smart pointers
Smart pointers are conceptually the same as pointers.
Implemented as a template class:
Class that contains a pointer of a variable type.
Implements the *, ->, = operators, constructor and destructor.
template
public:
explicit auto_ptr(T* p = 0) : ptr(p) {}
};
~auto_ptr()
T& operator*()
T* operator->()
{delete ptr;}
{return *ptr;}
{return ptr;}
Classes, templates and explicit constructors will be explained later.
Smart pointers
Therefore instead of writing
void foo(){
MyClass* p(new MyClass); p->DoSomething(); delete p;
}
The programmer writes
void foo(){
auto_ptr
p->DoSomething();
}
Smart pointers
Here is an example of code which illustrates the situation of a dangling pointer:
MyClass* p(new MyClass);
MyClass* q = p;
delete p;
p->DoSomething();
p = NULL;
q->DoSomething();
// Watch out! p is now dangling!
// p is no longer dangling
// Ouch! q is still dangling!
Problem: p and q are both pointing at the same address space, which is problematic.
Smart pointers
For auto_ptr, this is solved by setting its pointer to NULL when it is copied:
template
auto_ptr
}
if (this != &rhs) {
delete ptr;
ptr = rhs.ptr;
rhs.ptr = NULL; }
return *this;
This implementation prevents a memory space from being pointed to by two different auto_ptr. But maybe we wanted that possibility. Using smart pointers brings its limitations.
There are different variations of smart pointers.
Passing parameters and returning values
Parameters can be passed to a function: Byvalue
A copy of the value is made, then the copied value is passed to the function.
For objects, the copy constructor is called by the runtime system to make the copy.
Thus, the value used in the function is local to the function.
Changing it in the function does not change the value passed from the calling function. Pass by value cannot accept a parameter that is an expression.
Bypointer
A copy of the pointer value is made, then passed to the function.
Thus, both functions are pointing to the same value; no copy of the value pointed to is made. Changing the pointed value in the called function will change the value in the calling function.
Byreference
Conceptually same as pass by pointer, except that the called function cannot change where the
received pointer is pointing.
Drawback: cannot pass NULL , as a reference cannot be NULL
Advantage: can accept unnamed values resulting from the evaluation of expressions as parameters: void f(const T& t); called as f(T(a, b, c)), or f(a+b)
Call by constant reference is very often used to save memory consumption to pass parameters that are not to be changed locally.
Passing parameters and returning values
================= C::C(); ================= address where c is stored : 0068F9B3
passing by value
C::C(C); =================
= pass_by_value
= address where val_c is stored: 0068F8B8
================= passing by reference ================= = pass_by_reference
= address of object that ref_c refers to: 0068F9B3
================= passing by pointer ================= = pass_by_pointer
= address of object pointed to by
ptr_c: 0068F9B3
================= passing/returning by value
C::C(C); ================= address where val_c is stored: 0068F8B8
C::C(C);
passing/returning by reference =================
= address of object that ref_c refers to: 0068F9B3
================= passing/returning by pointer =================
address of object pointed to by ptr_c: 0068F9B3
=================
class C {
public:
C() {cout << “C::C();” << endl;};C(int) { cout << “C::C(int);” << endl; }; C(C&) { cout << “C::C(C);” << endl; }};int main() {cout << “= = = = = = = = = = = = = = = = =” << endl; C c;cout << “= = = = = = = = = = = = = = = = =” << endl; cout << “address where c is stored : ” << &c << endl;cout << “passing by value” << endl; pass_by_value(c);cout << “passing by reference” << endl; pass_by_reference(c);cout << “passing by pointer” << endl; pass_by_pointer(&c);cout << “passing/returning by value” << endl;c = pass_and_return_by_value(c);cout << “passing/returning by reference” << endl; c = pass_and_return_by_reference(c);cout << “passing/returning by pointer” << endl; c = *pass_and_return_by_pointer(&c);return 0; } Passing parameters and returning values void pass_by_value(C val_c) {cout << “= = = = = = = = = = = = = = = = =” << endl;cout << “= pass_by_value ” << endl;cout << “= address where val_c is stored: ” << &val_c << endl << endl;cout << “= = = = = = = = = = = = = = = = =” << endl;}void pass_by_reference(C& ref_c) {cout << “= = = = = = = = = = = = = = = = =” << endl;cout << “= pass_by_reference ” << endl;cout << “= address of object that ref_c refers to: ” << &ref_c << endl << endl; cout << “= = = = = = = = = = = = = = = = =” << endl;}void pass_by_pointer(C* ptr_c) {cout << “= = = = = = = = = = = = = = = = =” << endl;cout << “= pass_by_pointer ” << endl;cout << “= address of object pointed to by ptr_c: ” << ptr_c << endl << endl; cout << “= = = = = = = = = = = = = = = = =” << endl;} Passing parameters and returning values C pass_and_return_by_value(C val_c) {cout << “= = = = = = = = = = = = = = = = =” << endl;cout << “address where val_c is stored: ” << &val_c << endl << endl;// returning by value returns a copy of the local object (copied using the copy constructor). return val_c;}C* pass_and_return_by_pointer(C* ptr_c) {cout << “= = = = = = = = = = = = = = = = =” << endl;cout << “address of object pointed to by ptr_c: ” << ptr_c << endl << endl;cout << “= = = = = = = = = = = = = = = = =” << endl;// Here we return a pointer to the object. //// Returning a pointer the original object is not really useful (though it avoids making a copy of the object).// Really useful if we want to receive an object, do some processing using it,// and return a new object created locally that resides on the heap,// or sometimes receive a null pointer and return a pointer to newly created object. //// if (ptr_c) {// process and change the passed object// } else {// do some processing and create a new pointer to a C // }return ptr_c;}C& pass_and_return_by_reference(C& ref_c) {cout << “= = = = = = = = = = = = = = = = =” << endl;cout << “= address of object that ref_c refers to: ” << &ref_c << endl << endl; cout << “= = = = = = = = = = = = = = = = =” << endl;// here we return the original reference //// Use this if you want the calling function to use the function call as a value part of an expression.// The most famous example of pass-and-return-by reference is the implementation// of stream extraction/insertion operators (<>). //
// Limitation: as a reference cannot be made to point to a new object or to be null,
// passing/returning by pointer is often the appropriate solution. return ref_c;
}
Contents
Data types
Variable declaration and initialization Type checking
Type coercion
Pointers
Strings
Strings
C++ provides following two types of string representations: C-style character strings
string class type introduced with Standard C++
Many libraries would define their own string type
You will encounter many different ways of declaring/manipulating strings.
C-style character strings
With C-style character strings, strings are arrays of characters terminated by a null character ( )
char greeting[6] = {H, e, l, l, o, };
char greeting[] = Hello;
strcat(s1,s2): concatenate string s2 onto the end of string s1
strlen(s1): returns the length of string s1
strcmp(s1,s2): returns lexical distance between s1 and s2
strchr(s1,ch): returns a pointer to the first occurrence of character ch in s1
strstr(s1,s2): returns a pointer to the first occurrence of string s2 in string s1
Strings: example of C-style strings
#include
#include
using namespace std;
int main (){
char str1[10] = Hello;
char str2[10] = World;
char str3[10];
int len ;
// copy str1 into str3: PROBLEM!!
strcpy(str3, str1);
cout << “strcpy(str3, str1) : ” << str3 << endl; // concatenates str1 and str2: PROBLEM!! strcat(str1, str2); cout << “strcat(str1, str2): ” << str1 << endl; // total length of str1 after concatenation len = strlen(str1); cout << “strlen(str1) : ” << len << endl;return 0; } Standard C++ string class The
Provides all the operations mentioned above, using a more natural object- oriented style.
Standard C++ string class
62
Concordia University Department of Computer Science and Software Engineering
Standard C++ string class: example
#include
#include
using namespace std;
int main (){
string str1 = Hello;
string str2 = World;
string str3;
int len ;
// copy str1 into str3
str3 = str1;
cout << “str3 : ” << str3 << endl;// concatenates str1 and str2str3 = str1 + str2;cout << “str1 + str2 : ” << str3 << endl;// total length of str3 after concatenation len = str3.size();cout << “str3.size() : ” << len << endl;// compare two stringscout << “str1 is lexically ” << (str1.compare(str2)) << ” away from str2″ << endl; return 0;}63Concordia University Department of Computer Science and Software Engineering References Y. Daniel Liang, Introduction to Programming with C++ (Chapter 1, 11, 13, 15), Peason, 2014. Bjarne Stroustrup, The C++ Programming Language (Chapter 6, 7, 11, 22), Addison-Wesley, 2013. TutorialsPoint. Learn C++ Programming Language (Chapter 17). cplusplus.com.
cppreference.com. initialization.
cplusplus.com. Pointers.
learncpp.com. Returning by value, reference, and address.
isocpp.org. References.
Joey Paquet COMP345 course Notes Concordia university.
Reviews
There are no reviews yet.