学习笔记之C++入门到精通(名师教学·手把手教会)【职坐标】_腾讯课堂 内联函数 默认参数的函数 函数重载 函数模版 类、对象和封装 构造函数与析构函数 static类成员 动态内存分配 拷贝构造函数 const关键字 友元函数与友元类 Valgrind内存检测工具 运算符重载

学习笔记之C++入门到精通(名师教学·手把手教会)【职坐标】_腾讯课堂
内联函数
默认参数的函数
函数重载
函数模版
类、对象和封装
构造函数与析构函数
static类成员
动态内存分配
拷贝构造函数
const关键字
友元函数与友元类
Valgrind内存检测工具
运算符重载

C++入门到精通(名师教学·手把手教会)【职坐标】_腾讯课堂

https://ke.qq.com/course/101465#term_id=100105503

https://github.com/haotang923/ke.qq.com.cpp

  • 函数调用需要建立栈内存环境,进行参数传递,并产生程序执行转移,这些工作都需要时间开销。
  • C++提供inline函数,减少函数调用的成本。编译器看到inline后,为该函数创建一段代码,以便在后面每次碰到该函数的调用都用同一段代码来替换。
  • 内联函数可以在一开始仅声明一次。
  • 内联函数必须在调用之前被声明或定义,因为它的代码必须在被替换之前已经生成被替换的代码。
  • 内联函数中,不能有复杂结构控制语句如swtich/while/for。否则编译将该函数视为普通函数那样产生函数调用代码。
  • 递归函数不能作为内联函数。
  • 内联函数只适合于1-5行小函数。对于较长的函数,函数调用和返回的开销相对来说微不足道,也没必要用内联函数实现。 
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 #define MAX(a, b) ((a) > (b) ? (a) : (b))
13 
14 #define WRONGSQUARE(x) (x * x)
15 #define SQUARE(x) ((x) * (x))
16 
17 inline int max(int a, int b)
18 {
19     return a > b ? a : b;
20 }
21 
22 int main()
23 {
24     int a = 55, b = 4;
25     
26     int c = max(a ++, b);
27     
28     cout << "c = " << c << endl;
29     cout << "a = " << a << endl;
30 
31     a = 55;
32     
33     int d = MAX(a ++, b); // a ++ > b ? a ++ : b;
34     
35     cout << "d = " << d << endl;
36     cout << "a = " << a << endl;
37 
38     int e = SQUARE(2 + 3); // ((2 + 3) * (2 + 3))
39     
40     cout << "e = " << e << endl;
41     
42     int f = WRONGSQUARE(2 + 3); // (2 + 3 * 2 + 3)
43     
44     cout << "f = " << f << endl;
45     
46     return 0;
47 }
View Code
  • 结果可以看出宏使用中出现了不可预期的结果,而内联函数不会。
c = 55
a = 56
d = 56
a = 57
e = 25
f = 11
Program ended with exit code: 0
View Code

默认参数的函数

  • 如果一个函数有多个默认参数,则行参分布中,默认参数应从右往左逐渐定义。而当调用参数时,只能从右往左匹配参数。
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 void foo(int i, int j = 5, int k = 10);
13 //void foo(int, int = 5, int = 10);
14 
15 int main()
16 {
17     foo(20);
18     foo(20, 30);
19     foo(20, 30, 40);
20     
21     return 0;
22 }
23 
24 void foo(int i, int j, int k)
25 {
26     cout << i << " " << j << " " << k << endl;
27 }
View Code
  • 结果
20 5 10
20 30 10
20 30 40
Program ended with exit code: 0
View Code

函数重载

  • 相同的函数名,但是行参个数或类型不同
  • 编译器不以行参名,返回值来区分
  • 预定义的宏
    • https://msdn.microsoft.com/zh-cn/library/b0084kay.aspx
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 int square(int x)
13 {
14     cout << __FILE__ << " " << __LINE__ << " " << __func__ << endl; // File / Line / Function
15     return x * x;
16 }
17 
18 double square(double x)
19 {
20     cout << __FILE__ << " " << __LINE__ << " " << __func__ << endl; // File / Line / Function
21     return x * x;
22 }
23 
24 int main()
25 {
26     cout << "square(10)
" << square(10) << endl;
27     cout << "suqare(1.1)
" << square(1.1) << endl;
28     
29     return 0;
30 }
View Code
  • 结果注意用到预定义宏__FILE__,__LINE__,__func__来输出文件,行数和函数
square(10)
/Users/hao/PROJECTS/LeetCode/LeetCode/main.cpp 14 square
100
suqare(1.1)
/Users/hao/PROJECTS/LeetCode/LeetCode/main.cpp 20 square
1.21
Program ended with exit code: 0
View Code
  • C语言头文件中的extern "C"
 1 //
 2 //  Header.h
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/3.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 //常见的C语言头文件格式
10 #ifndef Header_h
11 #define Header_h
12 
13 #ifdef __cplusplus
14 extern "C" {
15 #endif
16 
17 // C语言的函数在C++中调用
18 void sample();
19 
20 #ifdef __cplusplus
21 }
22 #endif
23 
24 #endif /* Header_h */
View Code

函数模版

  • 对于具有各种参数类型,相同个数,相同顺序的同一函数(重载函数),如果用宏定义来写,则它不能检查数据类型,损害了类型安全性 。
  • 用模版可以减少代码量。
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 template <typename T>
13 T fAbs(T x)
14 {
15     return x < 0 ? -x : x;
16 }
17 
18 int main()
19 {
20     int n = -5;
21     double d = -5.5;
22     
23     cout << fAbs(n) << endl;
24     cout << fAbs(d) << endl;
25     
26     return 0;
27 }
View Code
5
5.5
Program ended with exit code: 0
View Result

类、对象和封装

  • struct安全性不好,任何人都能访问
  • class类不仅可以保护数据,还可以提供成员函数操作数据 
  • 类中定义的成员函数一般位内联函数,即使没有用inline标示
  • this指针代表当前对象占用内存空间的地址
  • OOP的三大特性封装(encapsulation)、多态(polymorphism)、继承(inheritance)

构造函数与析构函数

  • 只要类定义了一个构造函数,C++就不再提供默认的构造函数。
  • 与变量定义类似,在用默认构造函数创建对象时,如果创建的是全局对象或静态对象,则对象的值为0,否则对象值是随机的。
  • 构造函数的一个特殊之处是它没有返回类型,函数体中也不允许返回值,但可以有无值返回语句“return;”。
  • 如果创建一个5个元素的对象数组,则构造函数会被调用5次。
  • 析构函数只有一个,不能重载
 1 //
 2 //  Student.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/11.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef Student_hpp
10 #define Student_hpp
11 
12 #include <iostream>
13 using namespace std;
14 
15 class Student
16 {
17 public:
18     Student(int id = 0);
19     ~Student();
20 
21     const int getID() const;
22     void setID(int id);
23     const int getScore() const;
24     void setScore(int score);
25     
26 private:
27     int m_id;
28     int m_score;
29 };
30 
31 inline const int Student::getID() const
32 {
33     return m_id;
34 }
35 
36 inline void Student::setID(int id)
37 {
38     m_id = id;
39 }
40 
41 inline const int Student::getScore() const
42 {
43     return m_score;
44 }
45 
46 inline void Student::setScore(int score)
47 {
48     m_score = score;
49 }
50 
51 #endif /* Student_hpp */
Student.hpp
 1 //
 2 //  Student.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/11.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Student.hpp"
10 #include <iostream>
11 using namespace std;
12 
13 Student::Student(int id)
14 : m_score(0), m_id(id)
15 {
16     cout << "Student Constructor" << endl;
17 }
18 
19 Student::~Student()
20 {
21     cout << "Student destructor" << endl;
22 }
Student.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include "Student.hpp"
11 using namespace std;
12 
13 int main()
14 {
15 //    {
16     class Student std(10);
17     
18     cout << std.getID() << endl;
19     cout << std.getScore() << endl;
20 //    }
21     cout << "return from main()" << endl;
22     
23     return 0;
24 }
main.cpp
Student Constructor
10
0
return from main()
Student destructor
Program ended with exit code: 0
View Result

static类成员

  • 在static成员函数中不能使用this指针,因为this指针是属于对象的,而static成员函数是属于类的,不属于某一对象。
  • 即使没有实例化类的对象,static数据成员和成员函数仍然可以使用。
  • static成员的名字在类的作用域中,因此可以避免与其他的类的成员或者全局对象名字冲突。
  • 可以实施封装,static成员可以是私有成员,而全局对象不可以。

动态内存分配

  • C语言的动态内存分配:malloc/free函数
  • 内存区域
    • data area:全局变量、静态数据、常量 
    • code area:所有类成员函数和非成员函数代码
    • stack area:为运行函数而分配的局部变量、函数参数、返回数据、返回地址等
    • heap area:动态内存分配区
  • C++的运算符new/delete和malloc/free区别
    • 在堆上生成对象,需要自动调用构造函数。new可以做到,而malloc不行。
    • 在堆上生成的对象,在释放时需要自动调用析构函数。delete可以,而free不行。
    • new[]/delete[]生成和释放动态数组
    • new/delete,new[]/delete[]和malloc/free需要配对使用
    • new/delete是运算符,而malloc/free是函数调用
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <cstdlib> // malloc/free
11 using namespace std;
12 
13 class Test
14 {
15 public:
16     Test(int val = 0)
17     : m_val(val)
18     {
19         cout << "Test" << endl;
20     }
21     
22     ~Test()
23     {
24         cout << "~Test" << endl;
25     }
26     
27 private:
28     int m_val;
29 };
30 
31 int main ()
32 {
33     {
34         Test a;                 // "Test"
35     }                           // End of scope : "~Test"
36     cout << "end of }" << endl;
37     
38     Test *pVal = new Test();    // "Test"
39     delete pVal;                // "~Test"
40     pVal = nullptr;
41     
42     int *p = (int *)malloc(sizeof(int));
43     free(p);
44     p = nullptr;
45     
46     Test *pArray = new Test[2]; // twice "Test"
47     
48     delete[] pArray;            // twice call of destructor "~Test"
49     //delete pArray;            // memory leak
50     
51     pVal = new Test(10);        // "Test"
52     delete pVal;                // "~Test"
53     
54     return 0;
55 }
View Code
Test
~Test
end of }
Test
~Test
Test
Test
~Test
~Test
Test
~Test
Program ended with exit code: 0
View Result

拷贝构造函数

  • 当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式的调用拷贝构造函数。
  • 如果一个类没有定义拷贝构造函数,编译器会默认提供拷贝构造函数。
  • 编译器提供的默认拷贝构造函数的行为
    • 执行逐个成员初始化,将新对象初始化为原对象的副本。
    • “逐个成员”,指的是编译器将现有对象的每个非static成员,依次复制到正在创建的对象。
  • 为什么C++要定义拷贝构造函数浅拷贝:创建对象p2时,对象p1被复制给了p2,但资源并未复制。因此,p1和p2指向同一个资源。
    • 两个对象拥有统一资源,引起问题
  • 下例演示了浅拷贝带来的问题。使用默认的拷贝构造函数创建的对象p2,只是浅拷贝对象p,所以它们指向同一块内存空间。而程序结束时,依次析构p2,p3,p。由于析构p2时已经将内存空间释放,所以析构p时出错“pointer being freed was not allocated”。
 1 //
 2 //  person.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/26.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef person_hpp
10 #define person_hpp
11 
12 class Person
13 {
14 public:
15     Person(char * pName);
16     ~Person();
17     /*
18      Person(const Person &s);
19      Person& operator=(const Person &other);
20     */
21     
22     void Print();
23     
24 private:
25     char *name;
26 };
27 
28 
29 #endif /* person_hpp */
person.hpp
 1 //
 2 //  person.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/26.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "person.hpp"
10 
11 #include <iostream>
12 #include <cstring>
13 using namespace std;
14 
15 Person::Person(char *pN)
16 {
17     if (pN != nullptr) {
18         cout << "Constructing " << pN << " --->" << endl;
19 
20         int len = strlen(pN) + 1;
21         name = new char[len];
22         cout << "name = " << static_cast<void *>(name) << "
" << endl;
23         memset(name, 0, len);
24         strcpy(name, pN);
25     } else {
26         name = nullptr;
27     }
28 }
29 
30 Person::~Person()
31 {
32     cout << "Destrcuting Person --->" << endl;
33     
34     if (name != nullptr) {
35         Print();
36         delete [] name;
37         name = nullptr;
38     }
39 }
40 
41 void Person::Print()
42 {
43     cout << "pName = " << static_cast<void *>(name) << "
" << endl;
44 }
person.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 #include "person.hpp"
13 
14 int main ()
15 {
16     Person p("Joe");
17     Person p3("Tom");
18     
19     Person p2 = p; // 浅拷贝:使用编译器提供的默认的拷贝构造函数,指向同一块内存空间。导致析构时出现问题,同一块内存空间被析构两次。
20     
21     cout << "Print p --->" << endl;
22     p.Print();
23     
24     cout << "Print p2 --->" << endl;
25     p2.Print();
26     
27     return 0;
28 }
main.cpp
// 构造p
Constructing Joe --->
name = 0x100429ab0

// 构造p3
Constructing Tom --->
name = 0x100429d80

Print p --->
pName = 0x100429ab0

Print p2 --->
pName = 0x100429ab0

// 析构p2
Destrcuting Person --->
pName = 0x100429ab0

// 析构p3
Destrcuting Person --->
pName = 0x100429d80

// 析构p
Destrcuting Person --->
pName = 0x100429ab0

LeetCode(2399,0x100395340) malloc: *** error for object 0x100429ab0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
(lldb) 
View Result
  • 下例演示了深拷贝。注意到拷贝构造函数和重载赋值运算符里都将对象指向一块新的内存空间,所以析构时没有出现同一块内存空间被释放多次的问题。
 1 //
 2 //  person.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/26.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef person_hpp
10 #define person_hpp
11 
12 class Person
13 {
14 public:
15     Person(char * pName);
16     ~Person();
17     Person(const Person &s);
18     Person& operator= (const Person &other);
19     
20     void Print();
21     
22 private:
23     char *name;
24 };
25 
26 
27 #endif /* person_hpp */
person.hpp
 1 //
 2 //  person.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/26.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "person.hpp"
10 
11 #include <iostream>
12 #include <cstring>
13 using namespace std;
14 
15 Person::Person(char *pN)
16 {
17     if (pN != nullptr) {
18         cout << "Constructing " << pN << " --->" << endl;
19 
20         int len = strlen(pN) + 1;
21         name = new char[len];
22         cout << "name = " << static_cast<void *>(name) << "
" << endl;
23         memset(name, 0, len);
24         strcpy(name, pN);
25     } else {
26         name = nullptr;
27     }
28 }
29 
30 Person::~Person()
31 {
32     cout << "Destrcuting Person --->" << endl;
33     
34     if (name != nullptr) {
35         Print();
36         delete [] name;
37         name = nullptr;
38     }
39 }
40 
41 Person::Person(const Person &p)
42 {
43     cout << "Copy Constructor of Person --->" << endl;
44     
45     if (p.name != nullptr) {
46         int len = strlen(p.name) + 1;
47         name = new char[len];
48         cout << "name = " << static_cast<void *>(name) << "
" << endl;
49         memset(name, 0, len);
50         strcpy(name, p.name);
51     } else {
52         name = nullptr;
53     }
54 }
55 
56 // 注意“operator= ()”需要留一个空格,否则出错
57 Person& Person::operator= (const Person &other)
58 {
59     cout << "operator= --->
" << endl;
60     
61     // 防止自赋值
62     if (&other == this) {
63         return *this;
64     }
65     
66     if (name != nullptr) {
67         delete [] name;
68         name = nullptr;
69     }
70     
71     if (other.name != nullptr) {
72         int len = strlen(other.name) + 1;
73         name = new char[len];
74         memset(name, 0, len);
75         strcpy(name, other.name);
76     } else {
77         name = nullptr;
78     }
79     
80     return *this;
81 }
82 
83 void Person::Print()
84 {
85     cout << "pName = " << static_cast<void *>(name) << "
" << endl;
86 }
person.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 #include "person.hpp"
13 
14 int main ()
15 {
16     Person p("Joe");
17     Person p3("Tom");
18     
19     Person p2 = p; // 深拷贝:自定义拷贝构造函数,指向不同内存空间。析构时没有问题。
20     
21     cout << "Print p --->" << endl;
22     p.Print();
23     
24     cout << "Print p2 --->" << endl;
25     p2.Print();
26     
27     p2 = p3; // 重载赋值运算符
28     
29     cout << "Print p3 --->" << endl;
30     p3.Print();
31     
32     cout << "Print p2 --->" << endl;
33     p2.Print();
34 
35     return 0;
36 }
main.cpp
// 构造p
Constructing Joe --->
name = 0x103105370

// 构造p3
Constructing Tom --->
name = 0x1031053f0

// 构造p2
Copy Constructor of Person --->
name = 0x10310a520

Print p --->
pName = 0x103105370

Print p2 --->
pName = 0x10310a520

// 赋值给p2
operator= --->

Print p3 --->
pName = 0x1031053f0

Print p2 --->
pName = 0x100608e20

// 析构p2
Destrcuting Person --->
pName = 0x100608e20

// 析构p3
Destrcuting Person --->
pName = 0x1031053f0

// 析构p
Destrcuting Person --->
pName = 0x103105370

Program ended with exit code: 0
View Result
  • 如果想禁止一个类的拷贝构造,需要将拷贝构造函数声明为private
  • 何时需要定义拷贝构造函数 
    • 类数据成员有指针
    • 类数据成员管理资源(如打开一个文件)
    • 类需要析构函数来释放资源

const关键字

  • const限定指针类型const数据成员必须使用成员初始化列表进行初始化
    • const出现在*左边,表示被指物是常量
    • const出现在*右边,表示指针自身是常量
  • const成员函数
    • 类接口清晰,确定哪些函数可以修改数据成员
  • 使用const提高函数的健壮性下例演示了const修饰符作用,const数据成员初始化,const成员函数以及引用传递和值传递。
    • 用引用传递替代值传递:减少实参为对象时,拷贝构造/析构对象的代价。
    • 控制使用指针和引用传递的实参被意外修改
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 class Student
13 {
14 public:
15     Student(int id = 0)
16         : m_id(id) // Must use initialization list to initialize the const member
17     {
18         cout << "Student constructor --->
" << endl;
19         
20         // ERROR : Cannot assign to non-static data member 'm_id' with const-qualified type 'const int'
21         //m_id = id;
22     }
23     
24     ~Student()
25     {
26         cout << "Student destructor --->
" << endl;
27     };
28     
29     Student(Student &other)
30         : m_id(other.m_id)
31     {
32         cout << "Student copy constructor --->
" << endl;
33     }
34     
35     inline int getID() const
36     {
37         return m_id;
38     }
39     
40 private:
41      const int m_id;
42 };
43 
44 void foo(Student stu)
45 {
46     cout << __func__ << "
" << endl;
47 }
48 
49 void bar(const Student &stu)
50 {
51     cout << __func__ << "
" << endl;
52 }
53 
54 int main ()
55 {
56     const int a = 1;
57 
58     // ERROR : Cannot assign to variable 'a' with const-qualified type 'const int'
59     //a = 3;
60     
61     int b = 0;
62     
63     // const value
64     const int *p = &a;
65 
66     p = &b;
67     b = 3;
68     
69     cout << "p = " << *p << endl;
70     
71     // ERROR : Read-only variable is not assignable
72     //*p = 1;
73     
74     // const pointer
75     int * const p2 = &b;
76     
77     // ERROR : Cannot assign to variable 'p2' with const-qualified type 'int *const'
78     //p2 = &a;
79     
80     *p2 = 2;
81     
82     cout << "p2 = " << *p2 << "
" << endl;
83     
84     {
85         Student john(1001);
86         
87         cout << "Call foo(Student stu)
" << endl;
88         
89         foo(john);
90         
91         cout << "Call bar(const Student &stu)
" << endl;
92         
93         bar(john);
94     }
95 
96     cout << "Return from main" << endl;
97     
98     return 0;
99 }
View Code
p = 3
p2 = 2

// 构造对象
Student constructor --->

Call foo(Student stu)

// 值传递时对行参调用拷贝构造函数
Student copy constructor --->

foo

// 函数调用完毕,对行参调用析构函数
Student destructor --->

// 引用传递不会调用构造析构行参
Call bar(const Student &stu)

bar

// 对象生命周期结束,析构对象
Student destructor --->

Return from main
Program ended with exit code: 0
View Result

友元函数与友元类

  • 友元机制允许一个类对其非公有成员对访问授予指定的函数或类。友元关系时授予的,必须显示声明时友元
    • 友元的声明以friend开始
    • 只能出现在类定义的内部
    • 可以出现在类中的任何地方,不受其声明出现部分的访问控制(public/private/protect)影响 
  • 友元关系是不对称的
  • 友元会破坏封装
  • 下例演示了如何定义使用友元函数与友元类访问private变量
  1 //
  2 //  main.cpp
  3 //  LeetCode
  4 //
  5 //  Created by Hao on 2017/3/16.
  6 //  Copyright © 2017年 Hao. All rights reserved.
  7 //
  8 
  9 #include <iostream>
 10 using namespace std;
 11 
 12 // 类的前置声明
 13 class X;
 14 
 15 class Y
 16 {
 17 public:
 18     void f(X *);    // 对前置声明的类只能使用指针作为行参,因为在32位系统下,一个指针占4个字节是固定的,编译器可预判的
 19 //    void b(X);    // 用类作为行参则出错,因为编译器无法判断类的类型及大小
 20     
 21 private:
 22     X*  pX;
 23 };
 24 
 25 /*
 26 // ERROR : Variable has incomplete type 'X'
 27 void Y::b(X x)
 28 {
 29 }
 30 */
 31 
 32 class X
 33 {
 34 public:
 35     void initialize();
 36     void print();
 37     friend void fG(X *, int);   // Global friend
 38     friend void Y::f(X *);      // class member friend
 39     friend class Z;             // Entire class is a friend
 40     friend void h();            // Global friend
 41     
 42 private:
 43     int i;
 44 };
 45 
 46 void X::initialize()
 47 {
 48     i = 0;
 49 }
 50 
 51 void X::print()
 52 {
 53     cout << "i = " << i << "
" << endl;
 54 }
 55 
 56 void fG(X *x, int i)
 57 {
 58     x->i = i;
 59 }
 60 
 61 void Y::f(X *x)
 62 {
 63     x->i = 47;
 64 }
 65 
 66 class Z
 67 {
 68 public:
 69     void initialize();
 70     void g(X *x);
 71     
 72 private:
 73     int j;
 74 };
 75 
 76 void Z::initialize()
 77 {
 78     j = 100;
 79 }
 80 
 81 void Z::g(X *x)
 82 {
 83     x->i += j;
 84 }
 85 
 86 void h()
 87 {
 88     X x;
 89     
 90     x.i = 100;  // Direct data manipulation
 91     x.print();
 92 }
 93 
 94 int main ()
 95 {
 96     X x;
 97     
 98     x.initialize();
 99     x.print();
100     
101     // friend void fG(X *, int);   // Global friend
102     fG(&x, 9);
103     x.print();
104 
105     // friend void Y::f(X *);      // class member friend
106     Y y;
107     
108     y.f(&x);
109     x.print();
110 
111     // friend class Z;             // Entire class is a friend
112     Z z;
113     
114     z.initialize();
115     z.g(&x);
116     x.print();
117 
118     // friend void h();            // Global friend
119     h();
120     
121     return 0;
122 }
View Code
i = 0

i = 9

i = 47

i = 147

i = 100

Program ended with exit code: 0
View Result

Valgrind内存检测工具

  • 使用工具检查程序中存在的内存问题
  • 使用Valgrind完成相关功能
  • Valgrind Home
    • http://valgrind.org
  • valgrind_百度百科
    • https://baike.baidu.com/item/valgrind

运算符重载

  • 重载赋值运算符如果一个类提供了拷贝构造函数,那么也要提供一个重载的赋值运算函数
    • 如果一个类没有提供赋值运算函数,则默认提供一个。。。但是会存在浅拷贝的问题。
  • C++语言规定
    • 重载运算符要保持原有运算符的意义
    • 只能对已有的运算符重载,不能增加新的运算符
    • 重载的运算符不会改变原有的优先级和结合性
  • 运算符重载的方式以下四个运算符不能被重载::: / .* / . / ?
    • 成员函数
    • 友元函数
  • C++规定,参数说明都是内部类型时(e.g. int),不能重载。
  • 作为成员的运算符比作为友元的运算符,在声明和定义时,形式上少一个参数。
  • C++规定:=,(),[],-> 这四种运算符必须为成员形式
  • 下例演示了自增运算符的重载。可以看到前增量效率更高,因为它不需要拷贝构造来保存原有对象值。还需要注意后增量运算符中的参数int只为了区别前增量与后增量,除此之外没有任何作用。
 1 //
 2 //  Increase.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/28.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef Increase_hpp
10 #define Increase_hpp
11 
12 class Increase
13 {
14 public:
15     Increase(int val);
16     ~Increase();
17     
18     Increase& operator++ ();        // prefix, return reference
19     Increase operator++ (int val);  // postfix, return value
20     
21     int getVal() const
22     {
23         return m_val;
24     }
25     
26 private:
27     int m_val;
28 };
29 
30 #endif /* Increase_hpp */
Increase.hpp
 1 //
 2 //  Increase.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/28.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Increase.hpp"
10 
11 Increase::Increase(int val)
12     : m_val(val)
13 {
14 }
15 
16 Increase::~Increase()
17 {
18 }
19 
20 Increase& Increase::operator++ ()
21 {
22     ++ m_val;
23     return *this;
24 }
25 
26 Increase Increase::operator++ (int)
27 {
28     Increase ret(m_val);
29     ++ m_val;
30     
31     return ret;
32 }
Increase.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Increase.hpp"
10 
11 #include <iostream>
12 using namespace std;
13 
14 int main ()
15 {
16     Increase    val(100);
17     Increase    val2 = ++ val;
18     
19     cout << "val = " << val.getVal() << "
" << endl;
20     cout << "val2 = " << val2.getVal() << "
" << endl;
21     
22     Increase    val3 = val ++;
23     
24     cout << "val3 = " << val3.getVal() << "
" << endl;
25     cout << "val = " << val.getVal() << "
" << endl;
26     
27     return 0;
28 }
main.cpp
val = 101

val2 = 101

val3 = 101

val = 102

Program ended with exit code: 0
View Result

String类的运算符重载

  • 下例演示了如何编写String类的构造函数/析构函数/拷贝构造函数/运算符重载 
 1 //
 2 //  TString.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/28.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef TString_hpp
10 #define TString_hpp
11 
12 #include <iostream>
13 using namespace std;
14 
15 namespace T {
16 
17     class String
18     {
19     public:
20         String (const char * = nullptr);
21         ~String ();
22         
23         String (const String &);
24 
25         // 重载赋值运算符
26 
27         // String a; a = b;
28         String& operator= (const String &);
29         // String a; a = "hello";
30         String& operator= (const char *);
31         
32         String& operator+= (const String &);
33         String operator+ (const String &) const;
34         
35         String& operator+= (const char *);
36         String operator+ (const char *) const;
37         
38         inline const char * data() const
39         {
40             return m_data;
41         }
42         
43     private:
44         char *m_data;
45     };
46 
47 }
48 
49 
50 #endif /* TString_hpp */
TString.hpp
  1 //
  2 //  TString.cpp
  3 //  LeetCode
  4 //
  5 //  Created by Hao on 2017/12/28.
  6 //  Copyright © 2017年 Hao. All rights reserved.
  7 //
  8 
  9 #include "TString.hpp"
 10 
 11 #include <iostream>
 12 #include <cstring>
 13 using namespace std;
 14 
 15 namespace T
 16 {
 17 
 18     String::String(const char *str)
 19     {
 20         if (nullptr == str) {
 21             m_data = new char[1];
 22             *m_data = '