New blog location
New blog location
protected
and private
data members and methods. Example, given two classes A
and B
,B
is a friend of A
as follows:class A { friend class B; }
B
can access the private
and protected
data members and methods of A
.CheckingAccount
class from the previous article is updated as follows to include account number:checkingaccount.h
:#ifndef CHECKING_H #define CHECKING_H #include <string> using std::string; class CheckingAccount { public: CheckingAccount(std::string accountNumber, double balance); ~CheckingAccount(); void deposit(double amount); bool withdraw(double amount); double getBalance() const; string getAccountNumber(); private: std::string mAccountNumber; double mBalance; } #endif
checkingaccount.cpp
: Note that the ::
operator is the scope resolution operator.string CheckingAccount::getAccountNumber() { return mAccountNumber; } CheckingAccount::CheckingAccount(string accountNumber, double balance) //constructor { mBalance = balance; mAccountNumber = accountNumber; } void CheckingAccount::deposit(double amount) { mBalance += amount; } bool CheckingAccount::withdraw(double amount) { if((mBalance - amount) > 0) { mBalance -= amount; return true; } return false; } double getBalance() const { return mBalance; } //Destructor not implemented yet as there is no dynamic memory allocated.
Bank
class can be seen as a collection of CheckingAccount
s. Here is the first revision of the Bank
class:bank.h
:#ifndef BANK_H #define BANK_H #include <string> #include "checkingaccount.h" using std::string; class Bank { public: Bank(string name, int numAccounts); void depositIntoCheckingAccount(string accountNumber, double depositAmount); void withdrawFromCheckingAccount(string accountNumber, double withdrawAmount); CheckingAccount& getCheckingAccount(string accountNumber); string getName(); int getNumAccounts(); private: string mName; int mNumAccounts; CheckingAccount* mCheckingAccounts = nullptr; //in modern C++ will use vector<CheckingAccount> } #endif
bank.cpp
:Bank::Bank(string name, int numAccounts) : mName(name), mNumAccounts(numAccounts) { mCheckingAccounts = new CheckingAccount[numAccounts]; }
getCheckingAccount
, depositIntoCheckingAccount
, and withdrawFromCheckingAccount
are as follows:bank.cpp
:CheckingAccount& Bank::getCheckingAccount(string accountNumber) { CheckingAccount* checkingAccount = new CheckingAccount("",0.0); for(int i = 0; i < numAccounts; i++) { if(mCheckingAccounts[i].getAccountNumber == accountNumber) { return mCheckingAccounts[i]; } } return *checkingAccount; } void Bank::depositIntoCheckingAccount(string accountNumber, double depositAmount) { CheckingAccount& account = getCheckingAccount(accountNumber); if(account.accountNumber != "") { account.deposit(depositAmount); } else { cout << "Couldn't retrieve account with number "<< accountNumber; //handle error condition here. } } void Bank::withdrawFromCheckingAccount(string accountNumber, double depositAmount) { CheckingAccount& account = getCheckingAccount(accountNumber); if(account.accountNumber != "") { account.withdraw(depositAmount); } else { cout << "Couldn't retrieve account with number "<< accountNumber; //handle error condition here. } }
Bank
class:bank.cpp
:Bank::~Bank() { delete[] mCheckingAccounts; }
Bank b1("123",5); printBankInfo(b1); void printBankInfo(Bank b) { //code to print bank information }
b1
is passed to printBankInfo
, a shallow copy of the object is made, so the memory looks like the following:printBankInfo
exists, the copy of b1
is destroyed, so b1
is now pointing to freed memory. This is known as the dangling pointer problem. With the assignment operator, there is an additional problem that the original memory held by the overwritten object is orphaned and causes a memory leak.Bank::Bank(const Bank& src) : Bank(src.mName, src.mNumAccounts) { for(int k = 0 ; k < mNumAccounts; k++;) { mCheckingAccounts[k] = src.mCheckingAccounts[k]; } }
swap()
function is implemented as a friend of the Bank
class:class Bank { public: Bank& operator=(const Bank& rhs); friend void swap(Bank& first, Bank& second) noexcept; }
swap
swaps each data member using the std::swap
function in the <utility>
header file:void swap(Bank& first, Bank& second) noexcept { using std::swap; swap(first.mAccountNumber, second.mAcccountNumber); swap(first.mNumAccounts, second.mNumAccounts); swap(first.mCheckingAccounts, second.mCheckingAccounts); }
Bank& Bank::operator=(const Bank& rhs) { if(this == &rhs) { return *this; } Bank temp(rhs); swap(*this,temp); return *this; }
delete
, as follows:class Bank { public: //... Bank(const Bank& src) = delete; Bank& operator=(const Bank& rhs) = delete; }
&&
as part of the parameter list. Normally, a temporary object is a const type&
, but when the function is overloaded with an rvalue reference, a temporary object can be resolved to that overload. Example:void foo(string& message) { cout << "Message with lvalue reference" << endl; } void foo(string&& message) { cout << "Message with rvalue reference" << endl; }
foo()
accepting lvalue is called:string a = "hello"; string t = "world"; foo(a);
foo(a + t);
foo(a)
will cause an error because the rvalue reference cannot be bound to an lvalue. To cast an lvalue into an rvalue use std::move
:foo(std::move(a))
std::move()
is needed.noexcept
qualifier to state that they will not throw exceptions. Example using the Bank
class:class Bank { public: Bank(Bank&& src) noexcept; //move constructor Bank& operator=(Bank&& rhs) noexcept; //move assignment operator private: void cleanup() noexcept; void moveFrom(Bank &src) noexcept; }
Comments
Post a Comment