1. 程式人生 > 其它 >C++ 物件組合和繼承

C++ 物件組合和繼承

7. Composition(組合)

  • Composition: construct new object with existing objects.
  • It is the relationship of "has-a".
  • Ways of inclusion:
    • Fully
    • By reference
  • 示例:
class Person { ... };

// 貨幣
class Currency { ... };

// 儲蓄戶口
class SavingsAccount {
pubilc:
    SavingsAccount (const char* name, const char* address, int cents);
    ~SavingsAccount();
    void print();
private:
    // Fully inclusion
    Person m_saver;
    Currency m_balance;
};


// Person 和 Currency 初始化
SavingsAccount::SavingsAccount (const char* name, const char* address, int cents) : m_saver(name, address), m_balance(0, cents) {}


void SavingsAccount::print() {
    m_saver.print();
    m_balance.print();
}

8. Inheritance(繼承)

  • Inheritance: is to take the existing class, clone it, and then make additions and modifications to the clone.
  • "Is-a"
  • 示例1:訪問父類私有變數
#include <iostream>
using namespace std;

class A {
public:
    A():i(0) {
        cout << "A::A()" << endl;
    }
    ~A() {
        cout << "A::~A()" << endl;
    }
    void print() {
        cout << "A::print() ---》  " << i << endl;
    }
    void set(int ii) {
        i = ii;
    }
private:
    int i;
};

// 繼承, 其中 public 必需
class B : public A {
public:
    void f() {
        set(20);
        // 直接訪問 i,是會報錯
        // 父類私有的成員變數,子類不能直接訪問
        i = 30;
        print();
    }
};

int main()
{
    B b;
    b.set(10);
    b.print();
    b.f();

    return 0;
}
  • 示例2:訪問父類 protected 函式
#include <iostream>
using namespace std;

class A {
public:
    A():i(0) {
        cout << "A::A()" << endl;
    }
    ~A() {
        cout << "A::~A()" << endl;
    }
    void print() {
        cout << "A::print() ---》  " << i << endl;
    }
protected:
    void set(int ii) {
        i = ii;
    }
private:
    int i;
};

// 繼承, 其中 public 必需
class B : public A {
public:
    void f() {
        set(20);
        // 直接訪問 i,是會報錯
        // 父類私有的成員變數,子類不能直接訪問
        // i = 30;
        print();
    }
};

int main()
{
    B b;
    // set 調整為 protected
    // 子類可以使用,但是,main 函式中不能呼叫
    b.set(10);
    b.print();
    b.f();

    return 0;
}

8.1 子類和父類關係

  • 示例1:子類和父類構造順序
#include <iostream>

using namespace std;

class A {
public:
    A(int ii):i(ii) {
        cout << "A::A()" << endl;
    }
    ~A() {
        cout << "A::~A()" << endl;
    }
    void print() {
        cout << "A::print()" << i << endl;
    }
    void set(int ii) {
        i = ii;
    }
private:
    int i;
};

class B : public A {
public:
    B() : A(15) {
        cout << "B::B()" << endl;
    }
    ~B() {
        cout << "B::~B()" << endl;
    }
    void f() {
        set(20);
        print();
    }
};

int main()
{
    // 父類先初始化,然後才會初始化子類
    B b;
    b.print();
    b.f();
    
    return 0;
}
  • 父類和子類構造和析構順序:

  • 示例:子類和父類關係

#include <iostream>

class Employee {
public:
    Employee(const std::string& name, const std::string& ssn);

    const std::string& get_name() const;
    void print(std::ostream& out) const;
    void print(std::ostream& out, const std::string& msg) const;
protected:
    std::string m_name;
    std::string m_ssn;
};

Employee::Employee (const std::string& name, const std::string& ssn) :m_name(name), m_ssn(ssn)
{
    // initializer list sets up the values!
}

inline const std::string& Employee::get_name() const
{
    return m_name;
}

inline void Employee::print(std::ostream& out) const
{
    out << m_name << std::endl;
    out << m_ssn << std::endl;
}

inline void Employee::print(std::ostream& out, const std::string& msg) const
{
    out << msg << std::endl;
    print(out);
}

// Manager 類
class Manager : public Employee {
public:
    Manager(const std::string& name, const std::string& ssn, const std::string& title);
    const std::string title_name() const;
    const std::string& get_title() const;
    void print(std::ostream& out) const;
private:
    std::string m_title;
};

// 子類的構造,直接呼叫父類的建構函式
// 父類的構造過程,也必須放在 initializer list 裡面
Manager::Manager(const std::string& name, const std::string& ssn, const std::string& title = "") :Employee(name, ssn), m_title(title) {

}

inline void Manager::print(std::ostream& out) const {
    Employee::print(out); // call the base class print
    out << m_title << std::endl;
}

inline const std::string& Manager::get_title() const
{
    return m_title;
}

inline const std::string Manager::title_name() const
{
    return std::string(m_title + ": " + m_name);
}

int main() {
    Employee bob("Bob Jones", "555-44-0000");
    Manager bill("Bill Smith", "666-55-1234", "Important Person");

    std::string name = bill.get_name();
    std::cout << bill.title_name() << "\n" << std::endl;
    bill.print(std::cout);
    bob.print(std::cout);
    bob.print(std::cout, "Employee:");
    // Error:
    // Manager::print(std::ostream& out) 函式
    // 如果父類當中有 overload 函式,子類中出現同名函式時,子類僅保留子類自己的函式
    // bill.print(std::cout, "Manager: ");
    // 修正:
    bill.Employee::print(std::cout, "Manager: ");
}

8.2 Function overloading

  • Same functions with different arguments list.
// 示例
void print(char *str, int width); // #1
void print(double d, int width); // #2
void print(long l, int width); // #3
void print(int i, int width); // #4
void print(char *str); // #5

8.2.1 Default arguments

  • A default argument is a value given in the declaration that the compiler automatically inserts if you don't provide a value in the function call.
  • To define a function with an argument list, defaults must be added from right to left.

8.3 行內函數(inline)

  • the processing time required by a device prior to the execution of a command.

    • Push parameters
    • Push return address
    • Prepare return values
    • Pop all pushed
  • An inline function is expanded in place, like a preprocessor macro, so the overhead of the function call is eliminated.

  • 示例:

  • a.h

inline void f(int i, int j);
  • a.cpp
#include "a.h"

#include <iostream>
using namespace std;

inline void f(int i, int j) {
    cout << i << " " << j << endl;
}
  • main.cpp
#include "a.h"

int main()
{
    f(10, 10);
    
    return 0;
}
  • 編譯異常:

8.3.1 修正

  • a.h
#include <iostream>
using namespace std;

inline void f(int i, int j) {
    cout << i << " " << j << endl;
}
  • main.cpp
#include "a.h"

int main()
{
    f(10, 10);
    
    return 0;
}

8.3.2 Inline functions in header file

  • So you can put inline functions' bodies in header file. Then #include it where the function is needed.
  • Never be afraid of multi-definition of inline functions, since they have no body at all.
  • Definitions of inline functions are just declarations.

8.3.3 行內函數優缺點

  • Body of the called function is to be inserted into the caller.
  • This may expand the code size.
  • but deduces the overhead of calling time.
  • So it gains speed at the expenses of space.
  • In most cases, it is worth.
  • It is much better than macro in C. It checks the types of the parameters.
  • Any function you define inside a class declaration is automatically an inline.

參考資料: