C++面向对象高级编程(上-侯捷) | 字数总计: 3.8k | 阅读时长: 16分钟 | 阅读量:
本文是学习侯捷老师的C++面向对象高级编程(上)的课程笔记。
一、Class without pointer member(s) —— complex类 1、C++简介 2、头文件与类的声明 头文件的防卫式声明
作用:防止同一个文件被包含多次
#ifndef
1 2 3 4 5 6 #ifndef __COMPLEX__ #define __COMPLEX__ ... ... #endif
特点:
跨平台
可针对文件也可针对代码片段。
编译慢,有宏命名冲突的风险。
#pragmaonce
特点:
不跨平台
只能针对文件
编译快,无宏命名冲突的风险。
3、构造函数 如果一个类不带指针,则多半可以不写析构函数(还是推荐写上,万一你忘了呢?)。
在class body 内定义的函数自动inline,在类外要加inline关键字。inline函数可以让编译变快,你可以试着把所有函数都定义inline,但编译器inline不inline就不一定了,换句话说,你只是提交了一份inline“申请”,如果inline的函数简单,编译器就给你通过”申请“。
函数重载常常发生在构造函数中。
如果有一个构造函数已经有默认值,可以重载其他的构造函数,但不能重载与它冲突的那一个。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class complex {public : complex (double r =0 , double i =0 ) :re (r), im (i) {} complex ():re (0 ), im (0 ){} .... private : double re, im; }; complex c1; complex c2 () ;
4、参数传递与返回值 构造函数有时也会放在private中,singleton设计模式。
参数传递的三种方式,设计类成员函数时,要提前考虑好那些函数的数据会改变,如果不改变请加上const。
pass by value
pass by reference
pass by reference to const (推荐!)
返回值传递的时候,如果可以 ,建议使用return by reference。
友元(friend) 函数可以取得类的private中的数据,但不建议这么做,因为会破坏封装性。
但有一点请记住:相同class内的各objects互为友元! 所以下面类中的func函数 取用private数据合法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class complex {public : complex (double r=0 , double i=0 ):re (r), im (i){} int func (const complex& param) { return param.re + param.im; } private : double re, im; } { complex c1 (2 ,1 ) ; complex c2 () ; c2.func (c1); }
5、操作符重载与临时对象 (一)操作符重载之成员函数(this)
任何成员函数都有一个隐藏的pointer(即this) ,操作符重载也不例外。这个pointer(this)就指向调用者。对双目运算符来说,调用者就是左边的那个。
return by reference 语法分析: 传递者 无需知道接收者 是以 reference 形式 接收。
“+=” 等操作符的重载不能返回void
类型是因为:用户有可能会进行连加等多次连续操作 。
(二)操作符重载之非成员函数(无this)
与(一)的区别在于这种重载无this指针,它是全域/局函数。
(三)临时对象
类似(二)中的函数绝对不可以return by reference,因为他们返回的是一个local object 。所以只能return by value!
(四)千万不要把一些特殊的操作符重载为成员函数
比如:<< , >> ,
~小总结(设计一个class的注意事项)
构造函数使用初始化列表(initialization list) ;
函数该不该加 const;
参数传递 尽量考虑pass by reference,且考虑该不该加 const;
函数返回 是return by value 还是return by reference。
6、Complex类的完整实现 complex.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 #ifndef __MYCOMPLEX__ #define __MYCOMPLEX__ class complex ; complex& __doapl (complex* ths, const complex& r); complex& __doami (complex* ths, const complex& r); complex& __doaml (complex* ths, const complex& r); class complex { public : complex (double r = 0 , double i = 0 ): re (r), im (i) { } complex& operator += (const complex&); complex& operator -= (const complex&); complex& operator *= (const complex&); complex& operator /= (const complex&); double real () const { return re; } double imag () const { return im; } private : double re, im; friend complex& __doapl (complex *, const complex&); friend complex& __doami (complex *, const complex&); friend complex& __doaml (complex *, const complex&); }; inline complex&__doapl (complex* ths, const complex& r) { ths->re += r.re; ths->im += r.im; return *ths; } inline complex&complex::operator += (const complex& r) { return __doapl (this , r); } inline complex&__doami (complex* ths, const complex& r) { ths->re -= r.re; ths->im -= r.im; return *ths; } inline complex&complex::operator -= (const complex& r) { return __doami (this , r); } inline complex&__doaml (complex* ths, const complex& r) { double f = ths->re * r.re - ths->im * r.im; ths->im = ths->re * r.im + ths->im * r.re; ths->re = f; return *ths; } inline complex&complex::operator *= (const complex& r) { return __doaml (this , r); } inline double imag (const complex& x) { return x.imag (); } inline double real (const complex& x) { return x.real (); } inline complexoperator + (const complex& x, const complex& y){ return complex (real (x) + real (y), imag (x) + imag (y)); } inline complexoperator + (const complex& x, double y){ return complex (real (x) + y, imag (x)); } inline complexoperator + (double x, const complex& y){ return complex (x + real (y), imag (y)); } inline complexoperator - (const complex& x, const complex& y){ return complex (real (x) - real (y), imag (x) - imag (y)); } inline complexoperator - (const complex& x, double y){ return complex (real (x) - y, imag (x)); } inline complexoperator - (double x, const complex& y){ return complex (x - real (y), - imag (y)); } inline complexoperator * (const complex& x, const complex& y){ return complex (real (x) * real (y) - imag (x) * imag (y), real (x) * imag (y) + imag (x) * real (y)); } inline complexoperator * (const complex& x, double y){ return complex (real (x) * y, imag (x) * y); } inline complexoperator * (double x, const complex& y){ return complex (x * real (y), x * imag (y)); } complex operator / (const complex& x, double y){ return complex (real (x) / y, imag (x) / y); } inline complexoperator + (const complex& x){ return x; } inline complexoperator - (const complex& x){ return complex (-real (x), -imag (x)); } inline bool operator == (const complex& x, const complex& y){ return real (x) == real (y) && imag (x) == imag (y); } inline bool operator == (const complex& x, double y){ return real (x) == y && imag (x) == 0 ; } inline bool operator == (double x, const complex& y){ return x == real (y) && imag (y) == 0 ; } inline bool operator != (const complex& x, const complex& y){ return real (x) != real (y) || imag (x) != imag (y); } inline bool operator != (const complex& x, double y){ return real (x) != y || imag (x) != 0 ; } inline bool operator != (double x, const complex& y){ return x != real (y) || imag (y) != 0 ; } #include <cmath> inline complexpolar (double r, double t) { return complex (r * cos (t), r * sin (t)); } inline complexconj (const complex& x) { return complex (real (x), -imag (x)); } inline double norm (const complex& x) { return real (x) * real (x) + imag (x) * imag (x); } #endif
complex_text.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <iostream> #include "complex.h" using namespace std;ostream& operator << (ostream& os, const complex& x){ return os << '(' << real (x) << ',' << imag (x) << ')' ; } int main () { complex c1 (2 , 1 ) ; complex c2 (4 , 0 ) ; cout << c1 << endl; cout << c2 << endl; cout << c1+c2 << endl; cout << c1-c2 << endl; cout << c1*c2 << endl; cout << c1 / 2 << endl; cout << conj (c1) << endl; cout << norm (c1) << endl; cout << polar (10 ,4 ) << endl; cout << (c1 += c2) << endl; cout << (c1 == c2) << endl; cout << (c1 != c2) << endl; cout << +c2 << endl; cout << -c2 << endl; cout << (c2 - 2 ) << endl; cout << (5 + c2) << endl; return 0 ; }
二、Class with pointer member(s) ——String类 7、三大函数:拷贝构造、拷贝赋值、析构 (big three)
class with pointer members 必须自己编写拷贝构造、拷贝赋值和析构函数,否则编译器会使用默认的构造函数,就会造成浅拷贝和内存泄漏 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class String {public : String (const char * cstr = 0 ); String (const String& str); String& operator =(const String& str); ~String (); char * get_c_str () const { return m_data; } private : char * m_data; }; inline String::String (const char * cstr) { if (cstr) { m_data = new char [strlen (cstr) + 1 ]; strcpy_s (m_data, strlen (cstr)+1 , cstr); } else { m_data = new char [1 ]; *m_data = '\0' ; } } inline String::~String () { delete [] m_data; } inline String::String (const String& str) { m_data = new char [strlen (str.m_data) + 1 ]; strcpy_s (m_data, strlen (str.m_data) + 1 , str.m_data); } inline String& String::operator =(const String& str) { if (this == &str) return *this ; delete [] m_data; m_data = new char [strlen (str.m_data) + 1 ]; strcpy_s (m_data, strlen (str.m_data) + 1 , str.m_data); return *this ; } ostream& operator <<(ostream& os, const String& str) { os << str.get_c_str (); return os; }
拷贝赋值的经典四步曲
以s1 = s2
为例(s1、s2是两个字符串):
第一步:检测自我赋值 。(否则有可能导致未定义情况)
第二步:清理掉s1的数据。
第三步:为s1分配一块与s2一样大的内存空间
第四步:将s2拷贝到s1中。
8、堆、栈与内存管理 (一)Stack(栈)
概念: 是存在于某作用域 (scope) 的一块內存空间(memory space)。例如当你调用函数,函数本身即会形成一个stack 用來放置它所接收的参数,以及返回地址。 在函数本体 (function body) 內声明的任何变量,其所使用的內存块都取自上述 stack。
(二)heap(堆)
概念:或谓 system heap,是指由操作系统提供的一块 global 內存空间,程序可动态分配 (dynamic allocated) 从某中获得若干区块(blocks)。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Complex {}{ Complex c1 (1 , 2 ) ; Complex *p = new Complex (3 , 1 ); delete p; static Complex c2 (1 ,2 ) ; }
(三)new与delete
new : 先分配内存,在调用构造函数
delete: 先调用析构函数,在释放内存
array new (arr = new String[10];
)要搭配 array delete (delete [] arr;
)
侯捷老师提到,如果没有指针的数组,那直接delete arr;
不使用array delete也是没问题的,因为此时array的内存只有上图左侧那那一块。但是如果存在指针,就必须使用array delete,否则就会造成内存泄漏。因此为了统一且规范,那就 array new (arr = new String[10];
)要搭配 array delete (delete [] arr;
)。
注意!!!视频中侯捷老师提到:目前市面上的书籍资料都没有如此详细的对内存块的剖析。所以如果想了解,请移步原视频观看。
9、String类的实现 string.h 见7、三大函数:拷贝构造、拷贝赋值、析构(big three) 所示。
string_text.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include "string.h" #include <iostream> using namespace std;int main () { String s1 ("hello" ) ; String s2 ("world" ) ; String s3 (s2) ; cout << s3 << endl; s3 = s1; cout << s3 << endl; cout << s2 << endl; cout << s1 << endl; }
10、扩展补充:static、类模板、函数模板及其他 (一)static
(二)把构造函数放到private区域
Singleton设计模型
Meyers Singleton设计模式
(三)cout
(四)class template类模板
1 2 3 template <typename T>class complex { }; complex<double > c1;
(五)函数模板
编译器会对function template进行实参推导(argument deduction)
1 2 3 template <typename T>const T& min (const T& a, const T& b) { return b < a ? b : a; } c = min (a, b);
(六)namespace
三、Object Oriented Programming, Object Oriented Design(OOP,OOD) 侯捷老师提到,只要了解这三种关系,就可以做到让类与类之间建立关系。
Inheritance(继承)
Composition(复合),表示has-a
Delegation(委托)
11、复合、委托与继承 (一)Composition(复合),表示has-a
一个复合类的大小 = 该类数据大小 + 该类中复合类的大小
在复合 中,类和其复合的类是同时创建 的。
构造由内而外:Container的构造函数首先调用Component的默认构造函数,然后才执行自己的构造函数。
析构由外而内:Container的析构函数首先执行自己,然后才调用Component的析构函数。
(二)Delegation(委托),Composition by reference
创建一个指针,指向委托的那个类,让该类的功能,都在委托的那个类中实现。
委托其实和复合的功能很像,其实这就是对不同的实现分配到不同术语,你只需简单的记住,A类内含一个指针指向另一个类B(该类中实现了A的功能)就可以称作委托。
委托和复合他们中的类创建的时间不一样:在复合 中,类和其复合的类是同时创建 的;而在委托 中,我委托的那个类创建的时间我不清楚,反正一定比A类晚,即不同步创建 。
(三)Inheritance (继承), 表示is-a
子类继承父类所拥有函数,其实是继承父类的函数的使用权。
Inheritance继承关系下的构造和析构, 其实和复合关系下的构造和析构很像:
构造由内而外:Derived的构造函数首先调用Base的默认构造函数,然后才执行自己的构造函数。
析构由外而内:Derived的析构函数首先执行自己,然后才调用Base的析构函数。
12、虚函数与多态
non-virtual 函数:你不希望derived class 重新定义(override, 覆写) 它.
virtual 函数:你希望derived class 重新定义(override, 覆写) 它,且你對它已有默认定义。
pure virtual 函数:你希望derived class 一定要重新定义(override 覆写) 它,你对它沒有默认定义。
1 2 3 4 5 6 7 8 class Shape {public : virtual void draw ( ) const = 0 ; virtual void error (const stl::string& msg) ; int objectID ( ) const ; }; class Rectangle : public Shape{ ... };class Ellipse : public Shape{ ... };
(一)Inheritance with virtual
(二)Inheritance+Composition关系下的构造和析构
(三)Delegation (委托) + Inheritance (继承)
!!!注意:这一小节侯捷老师举了很多例子,希望认真观看原视频。