C++ 面向對象程序設計--侯捷

C++ 面向對象程序設計-part11.Header(头文件)的防卫式声明2.Header(头文件)的布局2.inline(内联)函数3.access level(访问级别)4.constructo(ctor,构造函数)5.const member functions(常量成员函数)6.参数传递:pass by value VS pass by reference7.返回值传递:return by value VS return by reference8.friend(友元)9.操作符重载10.三大函数11.heap(堆)与stack(栈)12.object的生命周期13.new和delete14.在OOP的三种关系C++ 面向對象程序設計-part21.conversion function,转换函数2.non-explicit-one-argument ctor,非显式单参数构造函数3.conversion function VS non-explicit-one-argument ctor4.explicit-one-argument ctor,显式的单参数构造函数5.pointer-like-classes,像指针的类,如智能指针,迭代器6.function-like classes,仿函数7.namespace经验谈8.class template,类模板或模板类9.function template,函数模板10.member template,成员模板11.specialization,模板特化12.partial specialization,模板偏特化--个数的偏13.partial specialization,模板偏特化--范围的偏14.template template parameter,模板模板参数15.variadic template(since C++11)16.reference,引用17.Composition(复合)关系下的构造和析构18.Inheritance(继承)关系下的构造和析构19.Composition+Inheritance(继承)关系下的构造和析构20.对象模型:关于vptr和vtbl21.对象模型:关于this指针21.对象模型:关于Dynamic Binding(动态绑定)22.关于const23.关于new和delete(未完成)

C++ 面向對象程序設計-part1

1.Header(头文件)的防卫式声明

#ifndef __COMPLeX__
#define __COMPLEX__
....
#endif

当我们在使用自己定义的头文件时,可能会多次包含头文件,这样会使得一段代码会重复出现。加上上图的防卫式声明,可以避免头文件被重复包含。

2.Header(头文件)的布局

#ifndef __COMPLEX__
#define __COMPLEX__
#include<cmath>

// 前置声明
class ostream;
class complex;

complex& __doapl(complex* ths, const complex& r);

// 类声明
class complex
{
  ...
};

// 类定义
complex::function...

#endif // !__COMPLEX__

2.inline(内联)函数

在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数

3.access level(访问级别)

  • public

  • protected

  • private

4.constructo(ctor,构造函数)

当我们new一个对象时,需要调用该对象的构造函数。

 

5.const member functions(常量成员函数)

我们应该养成一种习惯:将所有不会修改对象状态的成员函数都声明为const。

 

6.参数传递:pass by value VS pass by reference

C++中传递参数有三种方式:

  1. pass by value

  2. pass by reference(to const)

  3. pass by pointer

(传引用其实就是相当于传指针。)当参数的大小较大时,传引用会比传值会快。我们尽量选择pass by reference。

7.返回值传递:return by value VS return by reference

C++中返回值有三种方式:

  1. return by value

  2. return by reference(to const)

  3. return by pointer

我们尽量选择return by reference。

情形:

  • 当类似于+=操作符,应该选择return by reference。(需要将结果放到已经存在的对象中)

  • 当类似于+操作符,应该选择return by value。(不需要将结果放到已经存在的对象中,只需返回一个值)

8.friend(友元)

将某个函数或者成员变量声明为friend,这些称为友元,可以直接获取对应class的private数据。

注意:同一个class的各个objects互为友元。

9.操作符重载

操作符重载-成员函数

特点:

  • 应该return by reference

  • 函数的一个隐藏参数:this指针

适用操作符:例如+=,++

  • 绝大部分的单目运算符

  • 部分双目运算符:需要(可以)连串赋值。如:c1 += c2 += c3。

  • 操作符的左侧为object

操作符重载-非成员函数

特点:

  • 绝对是return by value。返回的是临时对象,return typename();

  • 操作符左侧的操作数可能是非对象

适用操作符:例如+,==,!=,(输入输出:>>和<<)

  • 个别单目运算符:+和-(正负)。

  • 部分双目运算符

10.三大函数

  • copy ctor(拷贝构造函数)

  • copy assignment operator(拷贝赋值函数)

  • 析构函数

11.heap(堆)与stack(栈)

  • stack

Stack,是存在於某作用域 (scope) 的一塊內存空間 (memory space)。例如當你調用函數,函數本身即 會形成一個 stack 用來放置它所接收的參數,以及返 回地址。 在函數本體 (function body) 內聲明的任何變量, 其所使用的內存塊都取自上述 stack。

  • heap

Heap,或謂 system heap,是指由操作系統提供的 一塊 global 內存空間,程序可動態分配 (dynamic allocated) 從某中獲得若干區塊 (blocks)。

12.object的生命周期

  • stack objects的生命周期

其生命在作用域 (scope) 結束之際結束。 這種作用域內的 object,又稱為 auto object,因為它會被「自動」清理。

  • static local objects的生命周期

其生命在作用域 (scope) 結束之後仍然存在,直到整個程序結束。

  • global objects的生命周期

其生命在整個程序結束之後 才結束。你也可以把它視為一種 static object,其作用域 是「整個程序」。

  • heap objects的生命周期

其生命 在它被 deleted 之際結束。

13.new和delete

  • new的操作:先分配memory,再调用ctor

  • delete的操作:先调用dtor,再释放memory

注意:array new一定要搭配array delete。

14.在OOP的三种关系

  • Composition(复合),表示has-a

  • Delegation(委托).Composition by reference

  • Inheritance(继承),表示is-a

 

C++ 面向對象程序設計-part2

1.conversion function,转换函数

转换函数:一个class类里的成员函数,但是其函数名为某个类型的名称typename,且为const函数。例如:在一个分数类(Fraction)中有一个转换函数double,将分数转换为double。代码如下:

class Fraction
{
   public:
       Fraction(int num, int den = 1) :
           m_numerator(num), m_denominator(den) {}
   
       // 转换函数
       operator double() const {
           return (double)(m_numerator / m_denominator);
      }
   private:
       int m_numerator;    // 分子
       int m_denominator;  // 分母
};

2.non-explicit-one-argument ctor,非显式单参数构造函数

Fraction类的构造函数就是这个类型的:只需要一个参数即可构造对象。

class Fraction
{
   public:
       Fraction(int num, int den = 1) :
           m_numerator(num), m_denominator(den) {}

       Fraction operator+(const Fraction& f) const {
           // 随便写的
           return Fraction(123, 12312);
      }
   private:
       int m_numerator;    // 分子
       int m_denominator;  // 分母
};

 

Fraction f(3, 5);
Fraction d2=f+4;

当一个Fraction与int相加,编译器会调用non-explicit ctor将4转换为Fraction,然后调用operator +。

 

3.conversion function VS non-explicit-one-argument ctor

当Fraction类同时拥有conversion function 和 non-explicit-one-argument ctor,执行如下代码会报错:

test.cpp:6:18: error: ambiguous overload for ‘operator+’ (operand types are ‘Fraction’ and ‘int’) Fraction d2=f+4; ^ test.cpp:6:18: note: candidates are: test.cpp:6:18: note: operator+(double, int) <built-in> In file included from test.cpp:1:0: Fraction.h:12:18: note: Fraction Fraction::operator+(const Fraction&) const Fraction operator+(const Fraction& f) const {

Fraction f(3, 5);
Fraction d2=f+4;

编译器报错:出现二义性ambiguous。有两种解释途径:

  1. 将4通过调用non-explicit-one-argument ctor转换为Fraction,然后调用operator +。

  2. 将f通过调用conversion function转换为double,然后与4相加还是doule,这个double传入non-explicit-one-argument ctor转化为Fraction。

4.explicit-one-argument ctor,显式的单参数构造函数

当我们在构造函数前加上explicit关键字后,编译程序报错:

test.cpp: In function ‘int main()’: test.cpp:6:18: error: conversion from ‘double’ to non-scalar type ‘Fraction’ requested Fraction d2=f+4;

要使得程序正确编译,需要将

Fraction d2=f+4;

改为:

Fraction d2=f+(Fraction)4;//显式调用

5.pointer-like-classes,像指针的类,如智能指针,迭代器

像智能指针这样的类指针类,一般有一个真正的指针,其中要关注pointer-like-classes的operator * 和 operator ->的实现,下面是一个例子:

template<class T>
class shared_ptr
{
public:
T& operator*() const {
return *px;
}
T* operator->() const {
}
shared_ptr(T* p) : px(p) {}

private:
T* px;
long* pn;
};

6.function-like classes,仿函数

顾名思义,就是一些关于作用类似于函数的类。这个类主要是重载operator ()。例如:

template<class T>
struct plus {
T operator() (const T& x, const T& y) const {
return x + y;
}
};
 

7.namespace经验谈

namespace,称为命名空间。其作用就是为了防止重名的一个。在写大型程序时,一定要使用namespace隔离。例子:

template<class T>
namespace jj01
{
void test();
}
namespace jj02
{
void test();
}

8.class template,类模板或模板类

通过在class的定义上方加上template,传入若干个模板参数,在类中引用这些模板参数。减少类的重复定义。下面是一个简单的例子:

template<class T>
class complex
{
public:
complex(T r = 0, T i = 0) :
re(r), im(i) {}
T real() const {return re;}
T imag() const {return im;}
private:
T re, im;
}

9.function template,函数模板

在函数定义的上方加入template<class T, ...>。使得函数的参数类型可以灵活变动,减少函数由于参数类型不同二重复定义大量作用类似的函数。

template<class T>
inline const T&(T& a, T& b) {
return a > b ? b : a;
}

10.member template,成员模板

在类或模板类中,其某些成员也是带有模板的,这些成员称为成员模板或模板成员。

11.specialization,模板特化

这里模板特化指的是,将template的模板参数具体为某种类型。例如:

template<class T>
struct hash {};

template<>
struct hash<char> {
size_t operator() (char x) const {return x;}
};

template<>
struct hash<int> {
size_t operator() (int x) const {return x;}
};

调用例子:

cout << hash<int>()(1000) << endl;

12.partial specialization,模板偏特化--个数的偏

当一个模板类有多个模板参数,我们可以选择将位于前面的参数先指定特定类型,再将后面的模板参数继续传下去。例如:

template<class T, class Alloc=...>
class vector
{
...
};
// 注意:template的参数
template<class Alloc=...>
class vector<bool, Alloc>
{
...
};

13.partial specialization,模板偏特化--范围的偏

当一个模板类的模板参数是一个class T,范围很大,传入任何参数都可以。此时可以将T的范围缩小,这就是模板特化--范围的偏。例如:

template<class T>
class C
{
...
};

// 这里的T可以换成其他的符号,无关紧要
template<class T>
class C<T*>
{
...
};

14.template template parameter,模板模板参数

模板参数中有参数又是模板,称之为模板模板参数。例如:

template<class T, template<class T> class Container>
class XCls
{
private:
Container<T> c;
public:
...
};

15.variadic template(since C++11)

标题是数量不定的模板参数。例子:下面的第一个print函数一定要写,否则会报错,因为传到最后会没有参数传入。

 

void print() {
   
}
template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args) {
   cout << firstArg << endl;
   print(args...);
}
print(7.5, "hello", 42);

16.reference,引用

其实,reference就是通过指针实现的。只不过,我们需要当reference当作别名使用。两个需要注意的点:

  1. 引用的大小与被引用的对象的大小一致

  2. 引用的地址与被引用的对象的地址一致

例子:

double x = 0;
   double* p = &x;
   double& r = x;

   cout << sizeof(x) << endl;//8
   cout << sizeof(p) << endl;//8
   cout << sizeof(r) << endl;//8
   cout << p << endl;//0x7ffc3890e088
   cout << *p << endl;//0
   cout << r << endl;//0
   cout << &x << endl;//0x7ffc3890e088
   cout<< &r << endl;//0x7ffc3890e088

17.Composition(复合)关系下的构造和析构

首先解释一下复合关系:当类A有一个成员的类型是类B,此时称类A和类B是复合关系。

调用构造函数的次序:当构造一个类A的对象时,首先会调用类B的构造函数,再执行类A的构造函数。

而调用析构函数的次序则是相反的:当销毁一个类A的对象时,会先调用类A的析构函数,再调用类B的析构函数。

记忆技巧:

构造就是穿衣服,需要从里到外穿;

而析构就是脱衣服,需要从外到里脱。

18.Inheritance(继承)关系下的构造和析构

调用构造函数的次序:在构造一个派生类对象时会先调用基类的构造函数,然后再调用派生类的构造函数。

调用析构函数的次序:在销毁一个派生类对象时,会先调用派生类的析构函数,然后再调用基类的析构函数。

19.Composition+Inheritance(继承)关系下的构造和析构

以下称基类为Base,派生类为Dervied,派生类所含的类成员为Component。

调用构造函数的次序:Derived的构造函数会先调用Base的构造函数,然后再调用Component的构造函数。

调用析构函数的次序:Dervied的析构函数会先调用自己的析构函数,再调用Component的析构函数,最后调用Base的析构函数。

20.对象模型:关于vptr和vtbl

  • vtbl:虚表。用于存放一个class的virtual function的函数指针

  • vptr:指向虚表的指针。所有拥有virtual function的class都有一个vptr。

关于对象模型,当一个class同时拥有static function、non-static function、virtual function时,前两者不是直接与class关联,而是放在class之外的。而后者是通过class的vptr指向vtbl找到的。

当派生类Derived继承基类Base时,派生类会继承基类Base的数据成员。(以public继承,public数据成员为例)

21.对象模型:关于this指针

当一个class拥有member function时,这些member function在代码层面是属于这个class的,但是在编译器层面,会将这些member function转换为global function,并增加一个指向该class的指针。例如:

class A
{
public:
void print() {
cout << data << endl;
}
private:
int data;
}

以上的成员函数print函数会转换为全局函数,如下(大致如下):

void print_A_nsf(A* this) {
cout << this->data << endl;
}

注意:任何带有static关键字的函数都不会有this指针。

21.对象模型:关于Dynamic Binding(动态绑定)

绑定什么?绑定虚函数。

而绑定又分为动态和静态:

  1. 静态绑定:当使用对象变量(非指针、非引用)调用某个虚函数时,此时调用的虚函数版本必定是该变量类型的虚函数。

  2. 动态绑定:只会出现在使用某个对象的指针或者引用调用虚函数才会出现。此时调用的虚函数版本有两种情况,如下:

#include<iostream>
using namespace std;
class Base
{
public:
virtual void f() {
cout << "Base" << endl;
}
};

class Derived : public Base
{
public:
virtual void f() {
cout << "Derived" << endl;
}
};

int main() {
   // 普通变量
   Base b;
   b.f();  // Base
   Derived d;
   d.f();  // Derived
   // 派生类转基类
   Base b1 = d;
   b1.f(); // Base
   // 引用
   Base& b2 = d;
   b2.f(); // Derived
   // 指针
   Base* b3 = &d;
   b3->f(); // Derived
}

22.关于const

const member functions(常量成员函数):指函数具有const属性,不会改变对象的状态。

如一个class 有一个成员函数定义如下:

double real() const {return re;}

上面这个函数就是const member functions(常量成员函数)。

  • 关于const是否属于函数签名的一部分?属于。

const function与const object:

 CONST OBJECTNON-CONST OBJECT
const member function 可以调用 可以调用
non-const member function 不可以调用 可以调用

注意:当一个member function的const版本和non-const版本同时存在,C++标准有一条规则:const object只会(只能)调用const 版本,non-const object只会(只能)调用non-const版本。

23.关于new和delete(未完成)

posted @ 2022-10-16 20:56  DavidJIAN  阅读(29)  评论(0)    收藏  举报