Transition to C++
3 Versions of C++
C++ 03,
C++ 11,
C++ 14,
C++ 17, etc.
Object-Oriented Programming (OOP)
- One core difference between C and C++ is that C++ supports OOD. In OOP, we think about our program in terms of objects, and we ask the objects to perform actions. The actual implementation of how to perform action is hidden from us.
- Two primary concepts in OOP are classes and objects.
- The only difference in C++ between a class and a struct is the default access control of its members.
- In C++, classes and structs can have functions declared inside of them. These functions are either called member functions or methods.
e.g.
class myClass {
int a;
int b;
int f (int x) {
return x * a - b;
}
}
Access Control
- In C++, members of a struct or a class can have their access restricted so that only code within the class can directly access them.
- There are three levels of access (also called visibility) that each member can have: public private or protected.
- PUBLIC: Can be accessed by any piece of code
- PRIVATE: Can be accessed only by code within that class
- PROTECTED: Will be discussed after discussing inheritance.
- In class, the default access is private. Therefore, only the public class can be accessed by any piece of code. But for struct, the default access is public.
Encapsulation
- For each method, there' ll be a default 'this' inside the function.
Const Methods
- When methods are created, the type 'this' is T * const this. But if we want to have const T * const this, we can add a const after the parentheses but before the curly brace.
e.g.
int getX () const {
return x;
}
- It's important because we can invoke a const method upon a const object but cannot invoke a non-const method upon a const object.
e.g.
void someMethod(const Point * p) {
int x = p->getX();
p->setX(42);
}
- Here, getX() is ok. But since setX() is not a non-const method, it is illegal because const declaration ensures we will not modify the contents of the object.
Plain Old Data (POD)
- C++ makes a distinction between classes (or structs) that are plain old data (POD) and classes that are not.
- All POD types have direct C analogs - they are sequences of fields in memory that we can copy around freely.
- If we can write in C, it is POD. If we cannot write in C, then it is probably not POD with one exception: declaring functions inside a class or struct, they do not make the type non-POD unless they are virtual functions.
- Functions are actually not inside the object. It resides in the memory and only the 'this' pointer tells where the object is. Objects have a direct C analog.
e.g.
class Myclass {
public:
int x;
int y;
int getSum() const {
return x + y;
}
}
we could also write in this way:
typedef struct {
int x;
int y;
} MyClass;
int getSum (const MyClass * const this) {
return this->x + this->y;
}
- sizeof(Myclass) would be the same whether we did C++ version or C version. And the offset of each field relative to the start of the object would be the same.
- Visiting restrictions (private) or mixing multiple visibilities (mix public with private) in a class makes it non-POD.
- For non-POD, we cannot work on the "raw" memory directly in a safe way like allocating, copying or deallocating that memory. We need to use the C++ operators which know how to work with non-POD types properly.
Static Members
- We may want relationship between different bankAccount. i.e. There may be a shared box for a particular field. We may want to know the account number. Meanwhile, we also want to know what next account number is.
- Under this situation, we can use the keyword static. It means there is one box shared by all instances of the bakaccount class, not one box per instance
e.g.
class Bankacccount {
private:
static unsigned long nextAccountNumber;
unsigned long accountNumber;
double balance;
public:
void initAccount() {
accountNumber = nextAccountNumber;
nextAccountNumber++;
balance = 0;
}
}
- BUT, static unsigned long nextAccountNumber; does not actually create this box. We still need one line of code outside of the class definition.
i.e.
unsigned long BankAccount::nextAccountNumber = 0;
- The above line of code actually creates the box and must be placed at the global scope - outside of any functions or classes.
- :: is the scope resolution operator, which allows us to specify a name inside of another named scope such as class.
- This box should be created before the main begins.
- We can also write methods in a class, but these methods cannot access non-static members because they don't have a 'this' pointer passed to them.
Classes Can Contain Other Types
- Classes can contain other types, including a typedef or even the declaration of an entire other class.
- We should set private for inner class.
- Inner class doesn't have direct access to non-static members of the outer class. But we can have a field in the inner class which is a pointer to the outer class type and initializing it to point at the appropriate outer-class instance.
Good OO Design
- Classes are nouns. Methods are verbs.
- Keep classes small and to their purposes.
- Be general as you can be, but no more
- Avoid "Manager" classes
References
- In C++, instead of *, we use & to represent a reference.
- When passing a reference, we use, e.g. swap(&a, &b) to pass reference
- But we implicitly dereference the reference. And we don't add & before a value to get its address.
i.e.
a = *a in pointer
- const reference may be initialized from something that is not an lvalue. BUT it is only legal when the reference is constant.
e.g.
void afunction(const int & x) {
//...
}
void bfunction() {
afunction(3);
}
- The above function is legal because x is a const int &. But if it is int &, it will be illegal.
- What actually happens is that compiler creates a temporary const in variable and passes the address of that variable.
- Reference and pointer are different types although they have similarities.
- We can define an operator that operates on two references but not on two pointers.
Namespaces
- In C++, functions and type names reside in a global scope and is visible throughout the entire program. But we can restrict the visibility of a function's name by declaring it as static.
- If a function is not declared static, then it must be unique in the entire program.
- If it is declared static, then it may not be used in any other compilation unit. But it introduces the problem of name collisions.
- C++ introduces a way to create named scopes, called namespaces. It can be used from anywhere in the program.
- Declaration can be placed inside of a namespace.
e.g.
namespace dataAnalysis {
class DataPoint {...};
class DataSet {...};
DataSet combineDataSets (DataSet * array, int n);
}
- 2 ways to reference a name declared inside a namespace.
1) scope resolution operator ::
If we want to use the vector class in the C++ standard library, which is in the std namespace, we can reference it by its fully qualified name:
std::vector.
2) open the namespace with the using keyword. It instructs the compiler to bring the names from the requested namespace into the current scope. e.g.:
using namespace std;
and then, we can just use vector to refer to std::vector
- using std::vector instead of using namespace std is typically preferable
Function Overloading
- Allowing multiple functions of the same name is called function overloading.
- It is legal if and only if this function can be distinguished by their parameter types. Differed in return type and parameter names are not a valid overloading.
- When using overload, we should follow the following guideline:
1) They should perform the same task but different types.
2) We should only overload functions in such a way that understanding what the best choice is for a particular call is straightforward.
Name Mangling
- Since in the process of C++ compiler, the names of the symbols the linker sees should be unique, it performs a name mangling to adjust the function names to encode the parameter type in formation, as well as what class and name
- In C, it does not mangle names. If we mix C and C++ code, then C++ compiler must be explicitly informed using extern "C", e.g.
extern "C" {
void someFunction();
int anotherFunciton();
}
- main function is treated specially as if it were declared extern "C" as it may be declared with or without parameters for the command line arguments and is called by the startup library, which is frequently written in C.
Operator Overloading
- C++ takes function overloading one step further, allowing operator overloading.
e.g.
class Point {
private:
int x;
int y;
public:
Point operator+(const Point & rhs) const {
Point ans;
ans.x = x + rhs.x;
ans.y = y + rhs.y;
return ans;
}
}
- We can write function declaration that defines the behavior of the operators when at least one user-defined type (e.g. class) is involved.
e.g.
Matrix & operator + (const Matrix & rhs) {
//...
}
- Here we define an overloading of the + operator inside of the point class. It takes 2 points as arguments. The first point is 'this'. The second operand is a const reference.
- In the case of operators that modify the object they are inside of, such as +=, they return a reference to that object. i.e. their return type is a reference to their own class type, and they return *this.
e.g.
Matrix & operator += (const Matrix & rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
-When returning *this, a reference is being initialized. Thus, the address is implicitly taken. i.e. the pointer is &*this, which is just 'this'.
- The reason why it returns the object is that a = b += c is legal.
Bool
- We should use bool for the type of parameters and variables that represent a boolean value.
Void Pointers
- In C, we can assign any pointer type to a void pointer and assign a void pointer to any pointer type without a cast. However, in C++ we need to cast explicitly.
Standard Headers
- In C, the header file ends with .h, but in C++, it is just, e.g. <vector>
- The scope of header file is global, it's not a preferred way to include the C standard library. Instead, C++ provides a header file that has the same name as the C header file but starts with a c and does not end with .h.
e.g.
C++ vs C
<cstdlib> vs <stdlib.h>
<cstdio> vs <stdio.h>
Code Organization
- In header file bank.h, we write the declaration, e.g.
class BankAccount {
private:
double balance;
public:
void deposit(double amount);
double withdraw(double desiredAmount);
double getBalance() const;
void initAccount();
}
- In corresponding cpp file, we would write:
#include "bank.h"
void BankAccount::deposit(double amount) {
balance += amount;
}
double BankAccount::withdraw(double desiredAmount) {
if (desiredAmount <= balance) {
balance -= desiredAmount;
return desiredAmount;
} else {
double actualAmount = balance;
balance = 0;
return actualAmount;
}
}
double BankAccount::getBalance() {
return balance;
}
void BankAccount::initAccount() {
balance = 0;
}
Default Values
- C++ allows default values.
e.g.
int f(int x, int y = 3, int z = 4, bool b = false);
- We should not abuse default values!! Only when we really need them.
- If we want to use them, we should declare default values in the header file. If not, we cannot use them even we declares them in the implementation.
nullptr
- In C, and C++03, we always use NULL to represent null pointer. However, it is not safe.
- C++11 improves on the definition of NULL by providing nullptr. It is of type nullptr_t. This is compatible with any other pointer type. (We can assign it to any pointer type, or compare a pointer of type nullptr_t to any other pointer) However, it is not compatible with int.
-Expression of type nullptr_t may still be implicitly converted to boolean.
summary& understanding after reading <<All of Programming>> Chapter 14 & Appendix E6.1
Comments
Post a Comment