CS计算机代考程序代写 compiler Java c++ COMP345:

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”: can’t 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 that’s 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(intVar)
▪ Explicitly “casts” intVar to double type
doubleVar = static_cast(intVar1)/intVar2;
▪ 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(expression)
General-purpose type casting
const_cast(expression)
Cast-out “constantness”
dynamic_cast(expression)
Runtime-checked conversion of pointers and references within a single class hierarchy. Used for downcasting from a superclass to a subclass
reinterpret_cast(expression)
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 ---------------------- area perimeter Circle ------------ radius diameter Rectangle ------------- 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(p)->getRadius() << endl; << static_cast(p)->getDiameter() << endl; << static_cast(p)->getWidth() << endl; << static_cast(p)->getHeight() << endl; << g.getArea() << endl; << g.getPerimeter() << endl; ▪ This successfully compiles, but will fail at runtime if the object passed was originally of a type that does not contain the members referred to in the code. ▪ static_cast makes a static (compile-time) type cast, but correct runtime behavior cannot be verified. Inheritance: upcasting and downcasting ▪ Use dynamic_cast to downcast into a subclass ▪ dynamic_cast works on pointers ▪ Does runtime checking to verify that the cast is successful ▪ Also deals with polymorphic types and virtual methods at runtime Conversion sequences ▪ When a variable is declared, it may be initialized using a value that is not of the same type as the variable. ▪ The compiler will attempt to find a type conversion sequence that enables it to convert the type of the value into the type declared for the variable. ▪ Two things can be used in a conversion sequence: ▪ conversion constructor: a constructor that takes a value of a type and creates an object of another type. A::A(int){...} ▪ conversion operator: a member operator that has the name of a type. A::operator int(){...} ▪ If you don’t want your constructors or operators to be used in a conversion sequence, you have to declare them as explicit in the class declaration ▪ explicit B(int); ▪ explicit operator bool() const; Contents ❑ Data types ❑ Variable declaration and initialization ❑ Type checking ❑ Type coercion ❑ ❑ Strings • pointer Arithmetic • pointers/const • references • smart pointers Pointers Pointers ▪ Variables contain a specific value, e.g., an integer. ▪ A pointer variable contains the memory address of a portion of memory that in turn contains a specific value. ▪ For any type T, T* is the type “pointer to T”, i.e. a variable of type T* can hold the address of an object of type T. int i = 99; int* p = &i; cout << *p << endl; ▪ Two operators on pointers: i (int) p (*int) 99 &i ▪ Dereferencing operator: *, e.g. *p refers to the object pointed to by the pointer. ▪ Address operator: &, e.g. &i refers to the address of the first memory cell holding a value. pointers ▪ Consider: int *p1, *p2, v1, v2; ▪ p1 and p2 are now uninitialized pointers (often called “wild pointers”). ▪ They point to a memory cell whose address corresponds to the value that was already in their cell before allocation. ▪ Pointer assignment: p1 = &v1; ▪ Sets pointer variable p1 to "point to" the address of variable v1 ▪ "p1 equals address of v1" ▪ Or "p1 points to v1" v1 (int) v2 (int) p1 (int*) p2 (int*) v1 (int) v2 (int) p1 (int*) &v1 p2 (int*) Pointers ▪ Value assignments v1 = 0; v1 (int) 0 v2 (int) p1 (int*) &v1 p2 (int*) v1 (int) 42 v2 (int) p1 (int*) &v1 p2 (int*) *p1 = 42; ▪ p1 and v1 refer to same memory cell ▪ Changing either the value pointed to by p1 or the value of v1 also changes the other variable’s value. Pointers ▪ Pointer assignment vs value assignment: int v1 = 42; int v2 = 9; int *p2 = &v2; int *p1 = &v1; ▪ Pointer assignment: p2 = p1; ▪ Assigns one pointer to another ▪ "Make p2 point to where p1 points“ ▪ Value assignment: *p2 = *p1; ▪ Assigns "value pointed to" by p1, to "value pointed to" by p2 v1 (int) 42 v2 (int) 9 p2 (int*) &v2 p1 (int*) &v1 v1 (int) 42 v2 (int) 9 p2 (int*) &v1 p1 (int*) &v1 v1 (int) 42 v2 (int) 42 p2 (int*) &v2 p1 (int*) &v1 Pointers ▪ Dynamicvariables ▪ Require the use of pointers ▪ Allocatedwithnewoperator,deallocatedwiththedeleteoperator ▪ Need to be allocated and destroyed explicitly while program runs ▪ C++ does not have a garbage collector ▪ Localvariables ▪ Declared within a function definition ▪ Not dynamic ▪ Allocated on the stack when code block is entered (e.g. function call) ▪ Destroyed when code block is exited (e.g. function call completes) ▪ Often called "automatic" variables ▪ Allocation and deallocation controlled by the runtime system as functions are called ▪ Uses a function call stack mechanism to automatically manage memory allocation/deallocation ▪ If pointers are declared in a function ▪ The pointer is managed as a local variable ▪ The value pointed to is dynamically allocated/deallocated Dynamic/automatic memory allocation: 29 example f2() f1(int) int global = 99; int f2(){ int f2x = 3; return (global/f2x); } int f1(int p){ // calling this function // creates a memory leak int* f1x = new int(10); return (p + *f1x + f2()); } int main(){ int x,y,z; y = 1; z = 2; for (int i = 1; i <= 10; i++){ x = f1(y); } } (int) f2x (int) x 3 (int) p (int*) f1x 1 44 (int) y 1 (int) z 2 global (int) global 99 10 Concordia University Department of Computer Science and Software Engineering main() (int) heap stack Pointers ▪ The operator new creates dynamically allocated values that can then be pointed to by pointer variables. ▪ The value created is a nameless pointer value. ▪ Allocated on the heap (also known as freestore) through the runtime system’s interaction with the operating system. ▪ All dynamically allocated variables need to be carefully managed by the programmer. ▪ C++ does not have garbage collection. ▪ Dynamically allocated variables need to be allocated and deallocated manually ▪ Similar to C’s malloc and free Pointers int *p1, *p2; p1 = new int; stack stack heap stack heap stack heap p1 (int*) p2 (int*) p1 (int*) p2 (int*) new int p1 (int*) p2 (int*) *p1 = 42; new int 42 p1 (int*) p2 = p1; p2 (int*) new int 42 Pointers *p2 = 53; stack heap stack heap stack heap p1 (int*) p2 (int*) new int 53 p1 (int*) p2 (int*) new int 53 new int p1 (int*) p2 (int*) p1 = new int; new int 53 new int 88 Pointers ▪ If the type used as parameter is of class type: ▪ Constructor is called for new object ▪ Can invoke different constructor with initializer arguments: MyClass *myPtr; myPtr = new MyClass(32.0, 17); ▪ Can still initialize non-class types: int *n; n = new int(17); //Initializes *n to 17 //That does NOT mean int is a class Pointers ▪ Pointers are full-fledged types ▪ Can be used just like other types ▪ Can be function parameters ▪ Can be returned from functions ▪ Example: int* findOtherPointer(int* p); ▪ This function declaration: ▪ Has "pointer to an int" parameter ▪ Returns "pointer to an int" Pointers ▪ Potential problem if freestore runs out of memory ▪ Older compilers: ▪ Test if null returned by call to new: int *p; p = new int; if (p == NULL){ cout << "Error: Insufficient memory.\n"; 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 pointers ▪ void 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 << '\n'; 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 declaration int *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 program’s process ▪ pointing to an address that contains read-only data ▪ Arbitrary value: ▪ pointing to a valid address that contains a valid but arbitrary integer value void, 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 library

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 class auto_ptr { T* ptr;
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(new MyClass);
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& auto_ptr::operator=(auto_ptr& rhs){
}
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 (‘\0’)
char greeting[6] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’};
char greeting[] = “Hello”;
provides many string manipulation functions ▪ strcpy(s1,s2): copy string s2 into string s1
▪ 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 standard C++ library provides a string class
▪ 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 str2 str3 = str1 + str2; cout << "str1 + str2 : " << str3 << endl; // total length of str3 after concatenation len = str3.size(); cout << "str3.size() : " << len << endl; // compare two strings cout << "str1 is lexically " << (str1.compare(str2)) << " away from str2" << endl; return 0; } 63 Concordia 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. class documentation.
▪ cppreference.com. initialization.
▪ cplusplus.com. Pointers.
▪ learncpp.com. Returning by value, reference, and address.
▪ isocpp.org. References.
▪ Joey Paquet COMP345 course Notes Concordia university.