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

时间:2023-03-09 04:32:46
学习笔记之C++入门到精通(名师教学·手把手教会)【职坐标】_腾讯课堂

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行小函数。对于较长的函数,函数调用和返回的开销相对来说微不足道,也没必要用内联函数实现。
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define WRONGSQUARE(x) (x * x)
#define SQUARE(x) ((x) * (x)) inline int max(int a, int b)
{
return a > b ? a : b;
} int main()
{
int a = , b = ; int c = max(a ++, b); cout << "c = " << c << endl;
cout << "a = " << a << endl; a = ; int d = MAX(a ++, b); // a ++ > b ? a ++ : b; cout << "d = " << d << endl;
cout << "a = " << a << endl; int e = SQUARE( + ); // ((2 + 3) * (2 + 3)) cout << "e = " << e << endl; int f = WRONGSQUARE( + ); // (2 + 3 * 2 + 3) cout << "f = " << f << endl; return ;
}
  • 结果可以看出宏使用中出现了不可预期的结果,而内联函数不会。
c =
a =
d =
a =
e =
f =
Program ended with exit code:

默认参数的函数

  • 如果一个函数有多个默认参数,则行参分布中,默认参数应从右往左逐渐定义。而当调用参数时,只能从右往左匹配参数。
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; void foo(int i, int j = , int k = );
//void foo(int, int = 5, int = 10); int main()
{
foo();
foo(, );
foo(, , ); return ;
} void foo(int i, int j, int k)
{
cout << i << " " << j << " " << k << endl;
}
  • 结果

Program ended with exit code: 

函数重载

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

函数模版

  • 对于具有各种参数类型,相同个数,相同顺序的同一函数(重载函数),如果用宏定义来写,则它不能检查数据类型,损害了类型安全性 。
  • 用模版可以减少代码量。
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; template <typename T>
T fAbs(T x)
{
return x < ? -x : x;
} int main()
{
int n = -;
double d = -5.5; cout << fAbs(n) << endl;
cout << fAbs(d) << endl; return ;
}
5.5
Program ended with exit code:

View Result

类、对象和封装

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

构造函数与析构函数

  • 只要类定义了一个构造函数,C++就不再提供默认的构造函数。
  • 与变量定义类似,在用默认构造函数创建对象时,如果创建的是全局对象或静态对象,则对象的值为0,否则对象值是随机的。
  • 构造函数的一个特殊之处是它没有返回类型,函数体中也不允许返回值,但可以有无值返回语句“return;”。
  • 如果创建一个5个元素的对象数组,则构造函数会被调用5次。
  • 析构函数只有一个,不能重载
 //
// Student.hpp
// LeetCode
//
// Created by Hao on 2017/12/11.
// Copyright © 2017年 Hao. All rights reserved.
// #ifndef Student_hpp
#define Student_hpp #include <iostream>
using namespace std; class Student
{
public:
Student(int id = );
~Student(); const int getID() const;
void setID(int id);
const int getScore() const;
void setScore(int score); private:
int m_id;
int m_score;
}; inline const int Student::getID() const
{
return m_id;
} inline void Student::setID(int id)
{
m_id = id;
} inline const int Student::getScore() const
{
return m_score;
} inline void Student::setScore(int score)
{
m_score = score;
} #endif /* Student_hpp */

Student.hpp

 //
// Student.cpp
// LeetCode
//
// Created by Hao on 2017/12/11.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Student.hpp"
#include <iostream>
using namespace std; Student::Student(int id)
: m_score(), m_id(id)
{
cout << "Student Constructor" << endl;
} Student::~Student()
{
cout << "Student destructor" << endl;
}

Student.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
#include "Student.hpp"
using namespace std; int main()
{
// {
class Student std(); cout << std.getID() << endl;
cout << std.getScore() << endl;
// }
cout << "return from main()" << endl; return ;
}

main.cpp

Student Constructor

return from main()
Student destructor
Program ended with exit code:

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是函数调用
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
#include <cstdlib> // malloc/free
using namespace std; class Test
{
public:
Test(int val = )
: m_val(val)
{
cout << "Test" << endl;
} ~Test()
{
cout << "~Test" << endl;
} private:
int m_val;
}; int main ()
{
{
Test a; // "Test"
} // End of scope : "~Test"
cout << "end of }" << endl; Test *pVal = new Test(); // "Test"
delete pVal; // "~Test"
pVal = nullptr; int *p = (int *)malloc(sizeof(int));
free(p);
p = nullptr; Test *pArray = new Test[]; // twice "Test" delete[] pArray; // twice call of destructor "~Test"
//delete pArray; // memory leak pVal = new Test(); // "Test"
delete pVal; // "~Test" return ;
}
Test
~Test
end of }
Test
~Test
Test
Test
~Test
~Test
Test
~Test
Program ended with exit code:

View Result

拷贝构造函数

  • 当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式的调用拷贝构造函数。
  • 如果一个类没有定义拷贝构造函数,编译器会默认提供拷贝构造函数。
  • 编译器提供的默认拷贝构造函数的行为
    • 执行逐个成员初始化,将新对象初始化为原对象的副本。
    • “逐个成员”,指的是编译器将现有对象的每个非static成员,依次复制到正在创建的对象。
  • 为什么C++要定义拷贝构造函数浅拷贝:创建对象p2时,对象p1被复制给了p2,但资源并未复制。因此,p1和p2指向同一个资源。
    • 两个对象拥有统一资源,引起问题
  • 下例演示了浅拷贝带来的问题。使用默认的拷贝构造函数创建的对象p2,只是浅拷贝对象p,所以它们指向同一块内存空间。而程序结束时,依次析构p2,p3,p。由于析构p2时已经将内存空间释放,所以析构p时出错“pointer being freed was not allocated”。
 //
// person.hpp
// LeetCode
//
// Created by Hao on 2017/12/26.
// Copyright © 2017年 Hao. All rights reserved.
// #ifndef person_hpp
#define person_hpp class Person
{
public:
Person(char * pName);
~Person();
/*
Person(const Person &s);
Person& operator=(const Person &other);
*/ void Print(); private:
char *name;
}; #endif /* person_hpp */

person.hpp

 //
// person.cpp
// LeetCode
//
// Created by Hao on 2017/12/26.
// Copyright © 2017年 Hao. All rights reserved.
// #include "person.hpp" #include <iostream>
#include <cstring>
using namespace std; Person::Person(char *pN)
{
if (pN != nullptr) {
cout << "Constructing " << pN << " --->" << endl; int len = strlen(pN) + ;
name = new char[len];
cout << "name = " << static_cast<void *>(name) << "\n" << endl;
memset(name, , len);
strcpy(name, pN);
} else {
name = nullptr;
}
} Person::~Person()
{
cout << "Destrcuting Person --->" << endl; if (name != nullptr) {
Print();
delete [] name;
name = nullptr;
}
} void Person::Print()
{
cout << "pName = " << static_cast<void *>(name) << "\n" << endl;
}

person.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; #include "person.hpp" int main ()
{
Person p("Joe");
Person p3("Tom"); Person p2 = p; // 浅拷贝:使用编译器提供的默认的拷贝构造函数,指向同一块内存空间。导致析构时出现问题,同一块内存空间被析构两次。 cout << "Print p --->" << endl;
p.Print(); cout << "Print p2 --->" << endl;
p2.Print(); return ;
}

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(,0x100395340) malloc: *** error for object 0x100429ab0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
(lldb)

View Result

  • 下例演示了深拷贝。注意到拷贝构造函数和重载赋值运算符里都将对象指向一块新的内存空间,所以析构时没有出现同一块内存空间被释放多次的问题。
 //
// person.hpp
// LeetCode
//
// Created by Hao on 2017/12/26.
// Copyright © 2017年 Hao. All rights reserved.
// #ifndef person_hpp
#define person_hpp class Person
{
public:
Person(char * pName);
~Person();
Person(const Person &s);
Person& operator= (const Person &other); void Print(); private:
char *name;
}; #endif /* person_hpp */

person.hpp

 //
// person.cpp
// LeetCode
//
// Created by Hao on 2017/12/26.
// Copyright © 2017年 Hao. All rights reserved.
// #include "person.hpp" #include <iostream>
#include <cstring>
using namespace std; Person::Person(char *pN)
{
if (pN != nullptr) {
cout << "Constructing " << pN << " --->" << endl; int len = strlen(pN) + ;
name = new char[len];
cout << "name = " << static_cast<void *>(name) << "\n" << endl;
memset(name, , len);
strcpy(name, pN);
} else {
name = nullptr;
}
} Person::~Person()
{
cout << "Destrcuting Person --->" << endl; if (name != nullptr) {
Print();
delete [] name;
name = nullptr;
}
} Person::Person(const Person &p)
{
cout << "Copy Constructor of Person --->" << endl; if (p.name != nullptr) {
int len = strlen(p.name) + ;
name = new char[len];
cout << "name = " << static_cast<void *>(name) << "\n" << endl;
memset(name, , len);
strcpy(name, p.name);
} else {
name = nullptr;
}
} // 注意“operator= ()”需要留一个空格,否则出错
Person& Person::operator= (const Person &other)
{
cout << "operator= --->\n" << endl; // 防止自赋值
if (&other == this) {
return *this;
} if (name != nullptr) {
delete [] name;
name = nullptr;
} if (other.name != nullptr) {
int len = strlen(other.name) + ;
name = new char[len];
memset(name, , len);
strcpy(name, other.name);
} else {
name = nullptr;
} return *this;
} void Person::Print()
{
cout << "pName = " << static_cast<void *>(name) << "\n" << endl;
}

person.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; #include "person.hpp" int main ()
{
Person p("Joe");
Person p3("Tom"); Person p2 = p; // 深拷贝:自定义拷贝构造函数,指向不同内存空间。析构时没有问题。 cout << "Print p --->" << endl;
p.Print(); cout << "Print p2 --->" << endl;
p2.Print(); p2 = p3; // 重载赋值运算符 cout << "Print p3 --->" << endl;
p3.Print(); cout << "Print p2 --->" << endl;
p2.Print(); return ;
}

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:

View Result

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

const关键字

  • const限定指针类型const数据成员必须使用成员初始化列表进行初始化
    • const出现在*左边,表示被指物是常量
    • const出现在*右边,表示指针自身是常量
  • const成员函数
    • 类接口清晰,确定哪些函数可以修改数据成员
  • 使用const提高函数的健壮性下例演示了const修饰符作用,const数据成员初始化,const成员函数以及引用传递和值传递。
    • 用引用传递替代值传递:减少实参为对象时,拷贝构造/析构对象的代价。
    • 控制使用指针和引用传递的实参被意外修改
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; class Student
{
public:
Student(int id = )
: m_id(id) // Must use initialization list to initialize the const member
{
cout << "Student constructor --->\n" << endl; // ERROR : Cannot assign to non-static data member 'm_id' with const-qualified type 'const int'
//m_id = id;
} ~Student()
{
cout << "Student destructor --->\n" << endl;
}; Student(Student &other)
: m_id(other.m_id)
{
cout << "Student copy constructor --->\n" << endl;
} inline int getID() const
{
return m_id;
} private:
const int m_id;
}; void foo(Student stu)
{
cout << __func__ << "\n" << endl;
} void bar(const Student &stu)
{
cout << __func__ << "\n" << endl;
} int main ()
{
const int a = ; // ERROR : Cannot assign to variable 'a' with const-qualified type 'const int'
//a = 3; int b = ; // const value
const int *p = &a; p = &b;
b = ; cout << "p = " << *p << endl; // ERROR : Read-only variable is not assignable
//*p = 1; // const pointer
int * const p2 = &b; // ERROR : Cannot assign to variable 'p2' with const-qualified type 'int *const'
//p2 = &a; *p2 = ; cout << "p2 = " << *p2 << "\n" << endl; {
Student john(); cout << "Call foo(Student stu)\n" << endl; foo(john); cout << "Call bar(const Student &stu)\n" << endl; bar(john);
} cout << "Return from main" << endl; return ;
}
p =
p2 = // 构造对象
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:

View Result

友元函数与友元类

  • 友元机制允许一个类对其非公有成员对访问授予指定的函数或类。友元关系时授予的,必须显示声明时友元
    • 友元的声明以friend开始
    • 只能出现在类定义的内部
    • 可以出现在类中的任何地方,不受其声明出现部分的访问控制(public/private/protect)影响 
  • 友元关系是不对称的
  • 友元会破坏封装
  • 下例演示了如何定义使用友元函数与友元类访问private变量
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; // 类的前置声明
class X; class Y
{
public:
void f(X *); // 对前置声明的类只能使用指针作为行参,因为在32位系统下,一个指针占4个字节是固定的,编译器可预判的
// void b(X); // 用类作为行参则出错,因为编译器无法判断类的类型及大小 private:
X* pX;
}; /*
// ERROR : Variable has incomplete type 'X'
void Y::b(X x)
{
}
*/ class X
{
public:
void initialize();
void print();
friend void fG(X *, int); // Global friend
friend void Y::f(X *); // class member friend
friend class Z; // Entire class is a friend
friend void h(); // Global friend private:
int i;
}; void X::initialize()
{
i = ;
} void X::print()
{
cout << "i = " << i << "\n" << endl;
} void fG(X *x, int i)
{
x->i = i;
} void Y::f(X *x)
{
x->i = ;
} class Z
{
public:
void initialize();
void g(X *x); private:
int j;
}; void Z::initialize()
{
j = ;
} void Z::g(X *x)
{
x->i += j;
} void h()
{
X x; x.i = ; // Direct data manipulation
x.print();
} int main ()
{
X x; x.initialize();
x.print(); // friend void fG(X *, int); // Global friend
fG(&x, );
x.print(); // friend void Y::f(X *); // class member friend
Y y; y.f(&x);
x.print(); // friend class Z; // Entire class is a friend
Z z; z.initialize();
z.g(&x);
x.print(); // friend void h(); // Global friend
h(); return ;
}
i = 

i = 

i = 

i = 

i = 

Program ended with exit code: 

View Result

Valgrind内存检测工具

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

运算符重载

  • 重载赋值运算符如果一个类提供了拷贝构造函数,那么也要提供一个重载的赋值运算函数
    • 如果一个类没有提供赋值运算函数,则默认提供一个。。。但是会存在浅拷贝的问题。
  • C++语言规定
    • 重载运算符要保持原有运算符的意义
    • 只能对已有的运算符重载,不能增加新的运算符
    • 重载的运算符不会改变原有的优先级和结合性
  • 运算符重载的方式以下四个运算符不能被重载::: / .* / . / ?
    • 成员函数
    • 友元函数
  • C++规定,参数说明都是内部类型时(e.g. int),不能重载。
  • 作为成员的运算符比作为友元的运算符,在声明和定义时,形式上少一个参数。
  • C++规定:=,(),[],-> 这四种运算符必须为成员形式
  • 下例演示了自增运算符的重载。可以看到前增量效率更高,因为它不需要拷贝构造来保存原有对象值。还需要注意后增量运算符中的参数int只为了区别前增量与后增量,除此之外没有任何作用。
 //
// Increase.hpp
// LeetCode
//
// Created by Hao on 2017/12/28.
// Copyright © 2017年 Hao. All rights reserved.
// #ifndef Increase_hpp
#define Increase_hpp class Increase
{
public:
Increase(int val);
~Increase(); Increase& operator++ (); // prefix, return reference
Increase operator++ (int val); // postfix, return value int getVal() const
{
return m_val;
} private:
int m_val;
}; #endif /* Increase_hpp */

Increase.hpp

 //
// Increase.cpp
// LeetCode
//
// Created by Hao on 2017/12/28.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Increase.hpp" Increase::Increase(int val)
: m_val(val)
{
} Increase::~Increase()
{
} Increase& Increase::operator++ ()
{
++ m_val;
return *this;
} Increase Increase::operator++ (int)
{
Increase ret(m_val);
++ m_val; return ret;
}

Increase.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Increase.hpp" #include <iostream>
using namespace std; int main ()
{
Increase val();
Increase val2 = ++ val; cout << "val = " << val.getVal() << "\n" << endl;
cout << "val2 = " << val2.getVal() << "\n" << endl; Increase val3 = val ++; cout << "val3 = " << val3.getVal() << "\n" << endl;
cout << "val = " << val.getVal() << "\n" << endl; return ;
}

main.cpp

val = 

val2 = 

val3 = 

val = 

Program ended with exit code: 

View Result

String类的运算符重载

  • 下例演示了如何编写String类的构造函数/析构函数/拷贝构造函数/运算符重载
 //
// TString.hpp
// LeetCode
//
// Created by Hao on 2017/12/28.
// Copyright © 2017年 Hao. All rights reserved.
// #ifndef TString_hpp
#define TString_hpp #include <iostream>
using namespace std; namespace T { class String
{
public:
String (const char * = nullptr);
~String (); String (const String &); // 重载赋值运算符 // String a; a = b;
String& operator= (const String &);
// String a; a = "hello";
String& operator= (const char *); String& operator+= (const String &);
String operator+ (const String &) const; String& operator+= (const char *);
String operator+ (const char *) const; inline const char * data() const
{
return m_data;
} private:
char *m_data;
}; } #endif /* TString_hpp */

TString.hpp

 //
// TString.cpp
// LeetCode
//
// Created by Hao on 2017/12/28.
// Copyright © 2017年 Hao. All rights reserved.
// #include "TString.hpp" #include <iostream>
#include <cstring>
using namespace std; namespace T
{ String::String(const char *str)
{
if (nullptr == str) {
m_data = new char[];
*m_data = '\0';
} else {
int length = strlen(str);
m_data = new char[length + ];
strcpy(m_data, str);
}
} String::~String()
{
delete [] m_data;
} String::String(const String & other)
{
int length = strlen(other.m_data); m_data = new char[length + ];
strcpy(m_data, other.m_data);
} String& String::operator= (const String &other)
{
// 检查自赋值
if (this == &other) {
return *this;
} // 释放原有内存资源
delete[] m_data; int length = strlen(other.m_data);
m_data = new char[length + ];
strcpy(m_data, other.m_data); return *this;
} String& String::operator= (const char *other)
{
delete[] m_data; if (nullptr == other) {
m_data = new char[];
*m_data = '\0';
} else {
int length = strlen(other);
m_data = new char[length + ];
strcpy(m_data, other);
} return *this;
} String& String::operator+= (const T::String &other)
{
char *tmp = m_data;
int length = strlen(m_data) + strlen(other.m_data); m_data = new char[length + ];
strcpy(m_data, tmp);
strcat(m_data, other.m_data); delete [] tmp; return *this;
} String String::operator+ (const T::String &other) const
{
String result; result += *this;
result += other; return result;
} String& String::operator+= (const char *other)
{
String tmp(other); *this += tmp; return *this;
} String String::operator+ (const char *other) const
{
String result = *this; result += other; return result;
} } // end of namespace T

TString.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "TString.hpp" #include <iostream>
using namespace std; using namespace T; int main ()
{
String s1("hello "); String s2 = s1; String s3 = "world "; cout << "s1 = " << s1.data() << "\n" << endl; cout << "s2 = " << s2.data() << "\n" << endl; cout << "s3 = " << s3.data() << "\n" << endl; // String& operator= (const char *);
s1 = "hello world "; cout << "s1 = " << s1.data() << "\n" << endl; // String& operator= (const String &);
s3 = s1; cout << "s3 = " << s3.data() << "\n" << endl; // String& operator+= (const String &);
s1 += s3; cout << "s1 = " << s1.data() << "\n" << endl; // String& operator+= (const char *);
s3 += "!"; cout << "s3 = " << s3.data() << "\n" << endl; // String operator+ (const String &) const;
String s4 = s1 + s2; cout << "s4 = " << s4.data() << "\n" << endl; // String operator+ (const char *) const;
s4 = s1 + "hello "; cout << "s4 = " << s4.data() << "\n" << endl; return ;
}

main.cpp

s1 = hello 

s2 = hello 

s3 = world 

s1 = hello world 

s3 = hello world 

s1 = hello world hello world 

s3 = hello world !

s4 = hello world hello world hello 

s4 = hello world hello world hello 

Program ended with exit code: 

View Result

继承

  • C++中继承有三种方式:公有继承,私有继承,多重继承(保护继承比较少见)
  • 统一建模语言_百度百科
    • https://baike.baidu.com/item/统一建模语言/3160571?fromtitle=UML&fromid=446747
  • Download Astah Products | Astah.net在构造一个子类时,完成其父类部分的构造由父类的构造函数完成。
    • http://astah.net/download
  • 子类与父类的构造析构顺序:先构造父类,后构造子类;先析构子类,后析构父类。
  • 下例演示了公有继承,以及如何构造子类
 //
// Animal.hpp
// LeetCode
//
// Created by Hao on 2017/12/29.
// Copyright © 2017年 Hao. All rights reserved.
// #ifndef Animal_hpp
#define Animal_hpp #include <string>
using namespace std; namespace T
{
class Animal
{
public:
Animal(int age, string location);
~Animal(); void setAge(int age);
int getAge() const; string getLocation() const; protected:
void setLocation(string location); string m_location; private:
int m_age;
}; class Cat : public Animal
{
public:
Cat(int age, int color, string location);
~Cat(); int getColor() const;
void setColor(int color); void setCatLocation(string location); private:
int m_color;
}; class Dog : public Animal
{
public:
Dog(int age, int weight, string location);
~Dog(); int getWeight() const;
void setWeight(int weight); private:
int m_weight;
};
} #endif /* Animal_hpp */

Animal.hpp

 //
// Animal.cpp
// LeetCode
//
// Created by Hao on 2017/12/29.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Animal.hpp" #include <iostream>
using namespace std; namespace T
{
Animal::Animal(int age, string location)
: m_age(age), m_location(location)
{
cout << "Animal constructing --->\n" << endl;
} Animal::~Animal()
{
cout << "Animal destructing --->\n" << endl;
} int Animal::getAge() const
{
return m_age;
} void Animal::setAge(int age)
{
m_age = age;
} string Animal::getLocation() const
{
return m_location;
} void Animal::setLocation(string location)
{
cout << __func__ << " --->\n" << endl;
} Cat::Cat(int age, int color, string location)
: Animal(age, location), m_color(color)
{
cout << "Cat constructing --->\n" << endl;
} Cat::~Cat()
{
cout << "Cat destructing --->\n" << endl;
} int Cat::getColor() const
{
return m_color;
} void Cat::setColor(int color)
{
m_color = color;
} void Cat::setCatLocation(string location)
{
Animal::setLocation(location);
} Dog::Dog(int age, int weight, string location)
: Animal(age, location), m_weight(weight)
{
cout << "Dog constructing --->\n" << endl;
} Dog::~Dog()
{
cout << "Dog destructing --->\n" << endl;
} int Dog::getWeight() const
{
return m_weight;
} void Dog::setWeight(int weight)
{
m_weight = weight;
} } // end of namespace T

Animal.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Animal.hpp" using namespace T; int main ()
{
{
Cat cat(, , "house");
cat.setAge();
cat.setColor(); cat.setCatLocation("apartment");
} {
Dog dog(, , "home");
dog.setAge();
dog.setWeight();
} return ;
}

main.cpp

Animal constructing --->

Cat constructing --->

setLocation --->

Cat destructing --->

Animal destructing --->

Animal constructing --->

Dog constructing --->

Dog destructing --->

Animal destructing --->

Program ended with exit code: 

View Result

  • 不同继承方式的影响主要体现在
    • 派生类成员对基类成员的访问权限
    • 通过派生类对象对基类成员的访问权限
  • 里氏代换原则_百度百科
    • https://baike.baidu.com/item/里氏代换原则
    • 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
    • 在进行设计的时候,尽量从抽象类继承,而不是从具体类继承。如果从继承等级树来看,所有叶子节点应当是具体类,而所有的树枝节点应当是抽象类或者接口。当然这个只是一个一般性的指导原则,使用的时候还要具体情况具体分析。
    • 简单的理解为一个软件实体如果使用的是一个父类,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,软件里面,把父类都替换成它的子类,程序的行为没有变化。
  • 开闭原则_百度百科
    • https://baike.baidu.com/item/开闭原则
    • 开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。
  • 公有继承(public)
    • 基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可直接访问。
    • 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
    • 通过派生类对象只能访问基类的public成员。
  • 私有继承(private)
    • 基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可直接访问。
    • 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
    • 通过派生类的对象不能直接访问基类中的任何成员。
  • 多重继承
    • 可以为一个派生类指定多个基类,这样的继承结构称为多重继承或多继承
    • Java/C#中没有多继承,C++中也应避免使用
    • 当两个父类有同样的成员时会带来模糊性,这样导致了名称冲突(name collision),在编译时将予以拒绝,也称之为菱形继承
    • 可以在方法前说明基类,或者用虚继承来解决菱形继承问题
  • 虚继承 - *,*的百科全书
    • https://zh.wikipedia.org/wiki/虚继承
    • 虚继承 是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。
    • 举例来说:假如类A和类B各自从类X派生(非虚继承且假设类X包含一些数据成员),且类C同时多继承自类A和B,那么C的对象就会拥有两套X的实例数据(可分别独立访问,一般要用适当的消歧义限定符)。但是如果类A与B各自虚继承了类X,那么C的对象就只包含一套类X的实例数据。对于这一概念典型实现的编程语言是C++。
    • 这一特性在多重继承应用中非常有用,可以使得虚基类对于由它直接或间接派生的类来说,拥有一个共同的基类对象实例。避免由于带有歧义的组合而产生的问题(如“菱形继承问题”)。其原理是,间接派生类(C)穿透了其父类(上面例子中的A与B),实质上直接继承了虚基类X。
    • 这一概念一般用于“继承”在表现为一个整体,而非几个部分的组合时。在C++中,基类可以通过使用关键字virtual来声明虚继承关系。
  • 构造对象的规则需要扩展以控制多重继承。构造函数按下列顺序被调用:
    • 任何虚拟基类的构造函数按照它们被继承的顺序调用。
    • 任何非虚拟基类的构造函数按照它们被继承的顺序调用。
    • 任何成员对象的构造函数按照它们声明的顺序调用。
    • 再调用类自己的构造函数
  • 下例演示了虚继承以及多重继承的使用。注意使用了虚继承之后类的size大小的不同。
    • 虚函数、虚继承对sizeof的影响 - ****博客
      • http://blog.****.net/acb0y/article/details/8822983
 //
// Furniture.hpp
// LeetCode
//
// Created by Hao on 2017/12/31.
// Copyright © 2017年 Hao. All rights reserved.
// #ifndef Furniture_hpp
#define Furniture_hpp #include <iostream>
using namespace std; class Furniture
{
public:
Furniture(int weight = )
: m_weight(weight)
{
cout << "Furniture constructing --->\n" << endl;
} void setWeight(int weight)
{
m_weight = weight;
} int getWeight() const
{
return m_weight;
} private:
int m_weight;
}; // Two classes virtually inheriting Furniture
class Sofa : public virtual Furniture
{
public:
Sofa(int weight = ); void watchTV()
{
cout << __func__ << "\n" << endl;
}
}; class Bed : public virtual Furniture
{
public:
Bed(int weight = ); void sleep()
{
cout << __func__ << "\n" << endl;
}
}; // 多重继承
class SofaBed : public Sofa, public Bed
{
public:
SofaBed(); void foldout()
{
cout << __func__ << "\n" << endl;
}
}; #endif /* Furniture_hpp */

Furniture.hpp

 //
// Furniture.cpp
// LeetCode
//
// Created by Hao on 2017/12/31.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Furniture.hpp" // Should not set weight = 0, or else ERROR Redefinition of default argument
Sofa::Sofa(int weight)
: Furniture(weight)
{
cout << "Sofa constructing --->\n" << endl;
} Bed::Bed(int weight)
: Furniture(weight)
{
cout << "Bed constructing --->\n" << endl;
} SofaBed::SofaBed()
{
cout << "SofaBed constructing --->\n" << endl;
}

Furniture.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Furniture.hpp" int main ()
{
Furniture furniture;
Sofa sofa;
Bed bed;
SofaBed sofabed; cout << "sizeof(Furniture) : " << sizeof(Furniture) << "\n" << endl;
cout << "sizeof(Sofa) : " << sizeof(Sofa) << "\n" << endl;
cout << "sizeof(Bed) : " << sizeof(Bed) << "\n" << endl;
cout << "sizeof(SofaBed) : " << sizeof(SofaBed) << "\n" << endl; sofabed.watchTV(); sofabed.sleep(); sofabed.foldout(); return ;
}

main.cpp

Furniture constructing --->

Furniture constructing --->

Sofa constructing --->

Furniture constructing --->

Bed constructing --->

Furniture constructing --->

Sofa constructing --->

Bed constructing --->

SofaBed constructing --->

sizeof(Furniture) : 

sizeof(Sofa) : 

sizeof(Bed) : 

sizeof(SofaBed) : 

watchTV

sleep

foldout

Program ended with exit code: 

View Result

  • 下例演示了继承与组合,优先使用组合而不是继承。
 //
// Car.hpp
// LeetCode
//
// Created by Hao on 2018/1/3.
// Copyright © 2018年 Hao. All rights reserved.
// #ifndef Car_hpp
#define Car_hpp #include <iostream>
using namespace std; namespace T { class Engine {
public:
Engine(int id)
: m_id(id)
{
cout << "Engine constructing\n" << endl;
} ~Engine()
{
cout << "Engine destructing\n" << endl;
} void start()
{
cout << "Engine start\n" << endl;
} void stop()
{
cout << "Engine stop\n" << endl;
} private:
int m_id;
}; class Wheel {
public:
Wheel(int id)
: m_id(id)
{
cout << "Wheel constructing\n" << endl;
} ~Wheel()
{
cout << "Wheel destructing\n" << endl;
} void roll()
{
cout << "Wheel rolling\n" << endl;
} private:
int m_id;
}; // class Car : public Engine, public Wheel // 避免使用多继承
class Car {
public:
Car(Engine *, Wheel *, string, int); ~Car()
{
cout << "Car destructing\n" << endl;
}; void run();
void stop(); private:
Car(const Car&);
Car& operator= (const Car&); Engine *m_engine;
Wheel *m_wheel;
string m_name;
int m_price;
}; class Stero {
public:
Stero()
{
cout << "Stero constructing\n" << endl;
} ~Stero()
{
cout << "Stero destructing\n" << endl;
} void play()
{
cout << "Stero playing\n" << endl;
}
}; class Benchi : public Car {
public:
Benchi(Engine *, Wheel *, string, int, Stero *); ~Benchi()
{
cout << "Benchi destructing\n" << endl;
} void musicOn(); private:
Stero *m_stero;
}; class Transformer : public Car {
public:
Transformer(Engine *, Wheel *, string, int, bool); ~Transformer()
{
cout << "Transformer destructing\n" << endl;
} void fight();
void transform(); private:
bool m_val;
}; } #endif /* Car_hpp */

Car.hpp

 //
// Car.cpp
// LeetCode
//
// Created by Hao on 2018/1/3.
// Copyright © 2018年 Hao. All rights reserved.
// #include "Car.hpp" namespace T {
Car::Car(Engine *e, Wheel *w, string name, int price)
: m_engine(e), m_wheel(w), m_name(name), m_price(price)
{
cout << "Car constructing\n" << endl;
} void Car::run()
{
m_engine->start();
m_wheel->roll(); cout << "Car running\n" << endl;
} void Car::stop()
{
m_engine->stop(); cout << "Car stopping\n" << endl;
} Benchi::Benchi(Engine *e, Wheel *w, string name, int price, Stero *st)
: Car(e, w, name, price), m_stero(st)
{
cout << "Benchi constructing\n" << endl;
} void Benchi::musicOn()
{
Car::run();
m_stero->play(); cout << "Music on\n" << endl;
} Transformer::Transformer(Engine *e, Wheel *w, string name, int price, bool val)
:Car(e, w, name, price), m_val(val)
{
cout << "Transformer constructing\n" << endl;
} void Transformer::fight()
{
run(); cout << "Transformer fight\n" << endl;
} void Transformer::transform()
{
run(); cout << "Transformer transform\n" << endl;
}
}

Car.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Car.hpp"
using namespace T; int main ()
{
Engine e1();
Wheel w1();
Stero stero;
Benchi benchi(&e1, &w1, "benchi", , &stero); benchi.musicOn(); Transformer t(&e1, &w1, "optimusprime", , true); t.transform();
t.fight(); return ;
}

main.cpp

Engine constructing

Wheel constructing

Stero constructing

Car constructing

Benchi constructing

Engine start

Wheel rolling

Car running

Stero playing

Music on

Car constructing

Transformer constructing

Engine start

Wheel rolling

Car running

Transformer transform

Engine start

Wheel rolling

Car running

Transformer fight

Transformer destructing

Car destructing

Benchi destructing

Car destructing

Stero destructing

Wheel destructing

Engine destructing

Program ended with exit code: 

View Result

多态

  • 多态性是指“多种行为”
  • 同样的方法调用而执行不同操作、运行不同代码
  • 多态通过分离做什么和怎么做,从另一个角度将接口和实现进行分离
    • “封装”通过合并特征和行为来创建新的数据类型
    • “实现隐藏”通过将细节“私有化”把接口和实现进行分离
  • “多态”则消除了类型之间的耦合关系
  • LSP(Liskov替换原则):子类型必须能够替换掉它的基类型
  • 多态的概念基于对象引用的动态绑定特性,动态绑定是多态现象的根源
  • 多态实现过程
    • 子类重写父类的方法
    • 代码中向父类型变量发出消息(静态绑定)
    • 运行时,根据变量实际引用的对象类型决定调用哪个方法(动态绑定)
  • 静态绑定在编译器进行
  • 动态绑定在运行期进行
  • 虚函数与抽象类
  • 虚函数 - *,*的百科全书
    • https://zh.wikipedia.org/wiki/虚函数_(程序语言)
    • 虚函数是面向对象程序设计中的一个重要的概念。只能适用于指针和参考的计算机工程运算。当从父类中继承的时候,虚函数和被继承的函数具有相同的签名。但是在运行过程中,运行系统将根据对象的类型,自动地选择适当的具体实现运行。虚函数是面向对象编程实现多态的基本手段。
    • 虚函数在设计模式方面扮演重要角色。例如,《设计模式》一书中提到的23种设计模式中,仅5个对象创建模式就有4个用到了虚函数(抽象工厂、工厂方法、生成器、原型),只有单例没有用到。
    • 在Java语言中, 所有的方法默认都是"虚函数". 只有以关键字 final 标记的方法才是非虚函数.
    • 在 C# 语言中, 对基类中的任何虚方法必须用 virtual 修饰, 而派生类中由基类继承而来的重载方法必须用 override 修饰.
    • 纯虚函数或纯虚方法是一个需要被非抽象的派生类覆盖(override)的虚函数. 包含纯虚方法的类被称作抽象类; 抽象类不能被直接实例化。 一个抽象基类的一个子类只有在所有的纯虚函数在该类(或其父类)内给出实现时, 才能直接实例化. 纯虚方法通常只有声明(签名)而没有定义(实现),但有特例情形要求纯虚函数必须给出函数体定义.
    • 作为一个例子, 抽象基类"MathSymbol"可能提供一个纯虚函数 doOperation(), 和派生类 "Plus" 和 "Minus" 提供doOperation() 的具体实现. 由于 "MathSymbol" 是一个抽象概念, 为其每个子类定义了同一的动作, 在 "MathSymbol" 类中执行 doOperation() 没有任何意义. 类似的, 一个给定的 "MathSymbol" 子类如果没有 doOperation() 的具体实现是不完全的.
    • 虽然纯虚方法通常在定义它的类中没有实现, 在 C++ 语言中, 允许纯虚函数在定义它的类中包含其实现, 这为派生类提供了备用或默认的行为. C++的虚基类的虚析构函数必须提供函数体定义,否则链接时(linking)在析构该抽象类的派生实例对象的语句处会报错。
    • 在C++语言中, 纯虚函数用一种特别的语法[=0]定义(但 VS 也支持 abstract 关键字:virtual ReturnType Function()abstract;)
    • 纯虚函数的定义仅提供方法的原型. 虽然在抽象类中通常不提供纯虚函数的实现, 但是抽象类中可以包含其实现, 而且可以不在声明的同时给出定义[2]. 每个非抽象子类仍然需要重载该方法
  • 抽象类_百度百科
    • https://baike.baidu.com/item/抽象类
    • 抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。通常在编程语句中用 abstract 修饰的类是抽象类。在C++中,含有纯虚拟函数的类称为抽象类,它不能生成对象;在java中,含有抽象方法的类称为抽象类,同样不能生成对象。抽象类是不完整的,它只能用作基类。在面向对象方法中,抽象类主要用来进行类型隐藏和充当全局变量的角色。
    • 与具体类比较
      • 抽象类不能直接实例化,并且对抽象类使用new运算符会导致编译时错误。虽然一些变量和值在编译时的类型可以是抽象的,但是这样的变量和值必须或者为null,或者含有对非抽象类的实例的引用(此非抽象类是从抽象类派生的)
      • 允许(但不要求)抽象类包含抽象成员。
      • 抽象类不能被密封。
    • 与接口比较
      • 抽象类表示该类中可能已经有一些方法的具体定义,但是接口就仅仅只能定义各个方法的界面(方法名,参数列表,返回类型),并不关心具体细节。
      • 接口是引用类型的,和抽象类的相似之处有三点:
        • 不能实例化;
        • 包含未实现的方法声明;
        • 派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员)。
      • 抽象类与接口紧密相关。然而接口又比抽象类更抽象,这主要体现在它们的差别上:
        • 类可以实现无限个接口,但仅能从一个抽象(或任何其他类型)类继承,从抽象类派生的类仍可实现接口,从而得出接口是用来解决多重继承问题的。
        • 抽象类当中可以存在非抽象的方法,可接口不能,且它里面的方法只是一个声明必须用public来修饰没有具体实现的方法。
        • 抽象类中的成员变量可以被不同的修饰符来修饰,可接口中的成员变量默认的都是静态常量(static final)。
        • 抽象类是对象的抽象,然而接口是一种行为规范。
    • 标准c++没有abstract关键字,代之使用纯虚类实现类似的功能,详见词条“虚类”。在实现接口时,常写一个抽象类,来实现接口中的某些子类所需的通用方法,接着在编写各个子类时,即可继承该抽象类来使用,省去在每个都要实现通用的方法的困扰。
    • 为了让一个类成为抽象类,至少必须有一个纯虚函数。包含至少一个纯虚函数的类视为抽象类。将函数初始化为0使它们成为纯虚函数,没有0这个初使化器,它们仅仅是虚函数。
    • 抽象类对于提供模式、蓝图和后代类遵循的原则有用,如果遵循了蓝图的语义,后代类的行为可能按抽象类提供者和使用者所期望的那样。
    • 通过使用抽象类,C++程序员可以提供C++组件的规范,在它的构建中指导组件的实现者。
    • 在面向对象方法中,抽象类主要用来进行类型隐藏。构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。
  • 虚类_百度百科
    • https://baike.baidu.com/item/虚类
    • 含有虚函数的类是虚类,虚函数用关键字virtual声明。
    • 虚函数表明只有在程序使用到该函数时,才获得与调用对象对应的该函数的实现。
    • 含有纯虚函数的类是纯虚类,更多的是叫抽象类。纯虚类可以有成员变量。纯虚类不能实例化。
    • 虚函数必须是基类的非静态成员函数,其访问权限可以是protected或public
    • 虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数。
    • 纯虚函数没有函数体。
    • c++虚类相当与java里面的抽象类,与接口的不同之处如下:
      • 1、一个子类只能继承一个抽象类(虚类),但能实现多个接口;
      • 2、一个抽象类可以有构造方法,接口没有构造方法;
      • 3、一个抽象类中的方法不一定是抽象方法,即其中的方法可以有实现(有方法体),接口中的方法是抽象方法,不能有方法体,只有声明;
      • 4、一个抽象类可以是public、private、protected、default, 接口只有public和default;
      • 5、一个抽象类中的方法可以是public、private、protected、default, 接口中的方法只能是public。
      • 相同之处:都不能实例化。
  • 纯虚函数与接口类
  • 纯虚函数_百度百科
    • https://baike.baidu.com/item/纯虚函数
    • 虚函数是一种特殊的虚函数,在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
    • 纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。凡是含有纯虚函数的类叫做抽象类。这种类不能声明对象,只是作为基类为派生类服务。除非在派生类中完全实现基类中所有的的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。
    • 一般而言纯虚函数的函数体是缺省的,但是也可以给出纯虚函数的函数体(此时纯虚函数变为虚函数),这一点经常被人们忽视,调用纯虚函数的方法为baseclass::virtual function.
    • 多态性:指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。
      • a.编译时多态性:通过重载函数和运算符重载实现。
      • b运行时多态性:通过虚函数和继承实现。
    • 虚函数:虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态重载
    • 抽象类:包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。
  • 接口(软件接口)_百度百科
    • https://baike.baidu.com/item/接口/15422203#viewPageContent
    • 接口(软件类接口)是指对协定进行定义的引用类型。其他类型实现接口,以保证它们支持某些操作。接口指定必须由类提供的成员或实现它的其他接口。与类相似,接口可以包含方法、属性、索引器和事件作为成员。
    • 面向对象的接口
      • 在C++中,一个类被允许继承多个类。但是在Java以后的语言不被允许。这样,如果想继承多个类时便非常困难。所以开发方想出了新办法:接口。一个接口内,允许包含变量、常量等一个类所包含的基本内容。但是,接口中的函数不允许设定代码,也就意味着不能把程序入口放到接口里。由上可以理解到,接口是专门被继承的。接口存在的意义也是被继承。和C++里的抽象类里的纯虚函数是相同的。不能被实例化。
  • 接口类不能实例化,不能生成对象实例
  • 必须为多态基类声明virtual析构函数,否则用基类指针定义的派生类对象,在析构时只会调用基类析构函数,而不会调用派生类析构函数,从而造成内存泄漏。
  • 针对接口编程,而不是针对实现编程。
  • 下例演示了多态以及虚函数,虚析构函数的使用
 //
// Animal.hpp
// LeetCode
//
// Created by Hao on 2018/1/4.
// Copyright © 2018年 Hao. All rights reserved.
// #ifndef Animal_hpp
#define Animal_hpp #include <iostream>
using namespace std; namespace T
{
class Animal
{
public:
Animal()
{
cout << "Animal constructing\n" << endl; } // 为基类声明虚析构函数
virtual ~Animal()
{
cout << "Animal destructing\n" << endl;
} // Need to add "const" to avoid error Member function 'makeSound' not viable: 'this' argument has type 'const T::Animal', but function is not marked const
void makeSound() const
{
cout << "Animal make sound\n" << endl;
} // "virtual" 只需要在声明时加上,不需要在定义时加上
virtual void smile() const; /*
virtual void smile() const = 0; // pure virtual function
*/ /*
virtual void smile() const
{
cout << "Animal smile\n" << endl;
}
*/
}; class Dog : public Animal
{
public:
Dog()
{
cout << "Dog constructing\n" << endl;
} ~Dog()
{
cout << "Dog destructing\n" << endl;
} void makeSound() const
{
cout << "Dog make sound\n" << endl;
} void smile() const
{
cout << "Dog smile\n" << endl;
}
}; class Cat : public Animal
{
public:
Cat()
{
cout << "Cat constructing\n" << endl;
} ~Cat()
{
cout << "Cat destructing\n" << endl;
} void makeSound() const
{
cout << "Cat make sound\n" << endl;
} void smile() const
{
cout << "Cat smile\n" << endl;
}
};
} #endif /* Animal_hpp */

Animal.hpp

 //
// Animal.cpp
// LeetCode
//
// Created by Hao on 2018/1/4.
// Copyright © 2018年 Hao. All rights reserved.
// #include "Animal.hpp" // "virtual" 只需要在声明时加上,不需要在定义时加上
void T::Animal::smile() const
{
cout << "Animal smile\n" << endl;
}

Animal.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Animal.hpp"
using namespace T; void func(const Animal & animal)
{
cout << "Pass by reference ---> \n" << endl; animal.makeSound();
animal.smile();
} void foo(Animal * pAnimal)
{
cout << "Pass by pointer ---> \n" << endl; pAnimal->makeSound();
pAnimal->smile();
} void bar(Animal animal)
{
cout << "Pass by value ---> \n" << endl; animal.makeSound();
animal.smile();
} int main ()
{
{
Dog dog;
Cat cat; cout << "sizeof(Animal) : " << sizeof(Animal) << "\n" << endl;
cout << "sizeof(Dog) : " << sizeof(Dog) << "\n" << endl;
cout << "sizeof(Cat) : " << sizeof(Cat) << "\n" << endl; func(dog); func(cat); foo(&dog); foo(&cat); bar(dog); bar(cat);
} cout << "为多态基类声明虚析构函数 --->\n" << endl; {
Animal * pCat = new Cat; pCat->makeSound();
pCat->smile(); foo(pCat); // 如果不声明为虚析构函数,则只会调用基类Animal的析构函数,而不会调用派生类Cat的析构函数,造成内存泄漏
delete pCat;
} return ;
}

main.cpp

Animal constructing

Dog constructing

Animal constructing

Cat constructing

sizeof(Animal) : 

sizeof(Dog) : 

sizeof(Cat) : 

Pass by reference ---> 

Animal make sound

Dog smile

Pass by reference ---> 

Animal make sound

Cat smile

Pass by pointer ---> 

Animal make sound

Dog smile

Pass by pointer ---> 

Animal make sound

Cat smile

Pass by value ---> 

Animal make sound

Animal smile

Animal destructing

Pass by value ---> 

Animal make sound

Animal smile

Animal destructing

Cat destructing

Animal destructing

Dog destructing

Animal destructing

为多态基类声明虚析构函数 --->

Animal constructing

Cat constructing

Animal make sound

Cat smile

Pass by pointer ---> 

Animal make sound

Cat smile

// 如果不声明为虚析构函数,则只会调用基类Animal的析构函数,而不会调用派生类Cat的析构函数,造成内存泄漏
Cat destructing Animal destructing Program ended with exit code:

View Result

  • 下例笔试题演示了虚函数,虚继承的使用
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "iostream"
using namespace std; class Base
{
public:
char Value() { return 'A'; } virtual char VirtualValue() { return 'X'; }
}; class Derived : public Base
{
public:
char Value() { return 'U'; }
}; class VirtualDerived : virtual public Base
{
public:
char Value() { return 'Z'; } char VirtualValue() { return 'V'; }
}; int main ()
{
Base * p1 = new Derived(); Base * p2 = new VirtualDerived(); cout << p1->Value() << " " << p1->VirtualValue() << " " << p2->Value() << " " << p2->VirtualValue() << " " << endl; return ;
}

main.cpp

A X A V
Program ended with exit code:

View Result

类模版

  • 模版是泛型编程的基础:通过模版能快速建立具有类型安全的类库集合和函数集合,使用模版操作不同数据类型,从而避免为每一种数据类型产生一个单独的类或函数。
  • 类模版的作用
    • 将程序要处理的对象的类型参数化
    • 使程序可以处理不同类型的对象
  • 下例演示了模版的使用。
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; namespace nsT {
template <typename T> T min(T a, T b)
{
return (a < b) ? a : b;
}
} int main ()
{
int a = , b = ;
int c = min(a, b); cout << c << endl; double d = 1.0, e = 2.0;
double f = min(d, e); cout << f << endl; return ;
}
  • 下例演示了类模版的使用。
 //
// class_template.hpp
// LeetCode
//
// Created by Hao on 2018/1/7.
// Copyright © 2018年 Hao. All rights reserved.
// #ifndef class_template_hpp
#define class_template_hpp #include <iostream>
using namespace std; namespace nsT {
class Test {
public:
Test() {} private:
// Error : Calling a private constructor of class 'nsT::Test'
/*
Test(const Test &);
Test& operator= (const Test &);
*/
}; template <typename T>
class Example {
public:
Example(T val)
: m_val(val)
{
cout << "Example constructing\n" << endl;
} ~Example()
{
cout << "Example destructing\n" << endl;
} T get() const
{
return m_val;
} void set(T val)
{
m_val = val;
} private:
T m_val;
};
} // end of namespace nsT #endif /* class_template_hpp */

class_template.hpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; #include "class_template.hpp" using namespace nsT; int main ()
{
// Using int as data type
Example<int> val(); int data = val.get(); cout << "data = " << data << "\n" << endl; // Using double as data type
Example<double> val2(2.0); cout << val2.get() << "\n" << endl; Test test; // Using class as data type
Example<Test> val3(test); return ;
}

main.cpp

  • 类模版与模版类的区别
    • 类模版是模版的定义,不是一个实实在在的类,定义中用到通用类型参数。
    • 模版类是实实在在的类定义,是类模版的实例化。类定义中参数被实际类型所代替。
  • 下例演示了用类模版来定义一个通用堆栈,此时该通用堆栈还不是一个类定义,只是类定义的一个框架,即类模版。
 //
// Stack.hpp
// LeetCode
//
// Created by Hao on 2018/1/8.
// Copyright © 2018年 Hao. All rights reserved.
// #ifndef Stack_hpp
#define Stack_hpp #include <iostream>
using namespace std; namespace nsT {
template<typename T> class StackIterator; // 前置声明 template<typename T>
class Stack
{
public:
Stack()
: m_top()
{
cout << "Stack constructing\n" << endl; m_array[] = T();
} ~Stack()
{
cout << "Stack destructing\n" << endl;
} int size() const
{
return m_top;
} void push(const T&);
T pop();
friend class StackIterator<T>; // 友元类 private:
enum { SIZE = };
T m_array[SIZE];
int m_top;
}; template<typename T>
void Stack<T>::push(const T& val)
{
if (m_top < SIZE) {
m_array[m_top ++] = val;
}
} template<typename T>
T Stack<T>::pop()
{
if (m_top > )
return m_array[-- m_top];
else
return m_array[];
} template<typename T>
class StackIterator {
public:
StackIterator(Stack<T>& val)
: m_stack(val), m_index()
{
cout << "StackIterator constructing\n" << endl;
} ~StackIterator()
{
cout << "StackIterator destructing\n" << endl;
} T& operator++ (int) // post
{
int ret = m_index; if (m_index < m_stack.m_top - )
m_index ++; return m_stack.m_array[ret];
} private:
Stack<T>& m_stack;
int m_index;
};
}
#endif /* Stack_hpp */

Stack.hpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
using namespace std; #include "Stack.hpp" using namespace nsT; int main ()
{
Stack<int> stack; stack.push();
stack.push();
stack.push(); auto size = stack.size(); cout << "size of stack : " << size << "\n" << endl; StackIterator<int> iter(stack); for (auto i = ; i < size; i ++)
cout << iter ++ << "\n" << endl; cout << "pop : " << stack.pop() << "\n" << endl; return ;
}

main.cpp

STL

  • 将算法从特定的数据结构中抽象出来
  • C++的模版为泛型程序设计奠定了关键的基础
  • STL是泛型程序设计的一个范例,由一些可适应不同需求的集合类以及在这些数据集合上操作的算法构成
  • STL(模板库)_百度百科
    • https://baike.baidu.com/item/STL/70103?fr=aladdin
  • 容器的类别
    • 序列式容器:排列次序取决于插入时机和位置
    • 关联式容器:排列顺序取决于特定准则(平衡二叉树)
  • 向量(vector)属于序列式容器,用于容纳不定长的线性序列,提供对序列的快速随机访问(直接访问)。
  • 向量是动态结构,模拟动态数组,它的大小不固定,可以在程序运行时增加或减少。
  • vector的元素可以是任意类型,但必须具备赋值和拷贝能力,具有public的拷贝构造函数和重载的赋值运算符。
  • vector的大小(size)和容量(capacity)通常是不同的,capacity返回vector实际能容纳的元素数量。如果超过这个数量需要重新配置内部存储器。
  • vector(Java与C++语言中的对象)_百度百科
    • https://baike.baidu.com/item/vector/3330482?fr=aladdin
  • 下例演示了vector的使用
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
#include <vector>
using namespace std; int main()
{
vector<int> coll; cout << "capacity : " << coll.capacity() << "\n" << endl;
cout << "size : " << coll.size() << "\n" << endl; for (auto i = ; i < ; i ++)
coll.push_back(i); cout << "capacity : " << coll.capacity() << "\n" << endl;
cout << "size : " << coll.size() << "\n" << endl; cout << coll[] << endl; return ;
}

vector.cpp

capacity : 

size : 

capacity : 

size : 

Program ended with exit code: 

View Result

  • 迭代器是面向对象版本的指针
    • 指针可以指向内存中的一个地址
    • 迭代器可以指向容器中的一个位置,用来遍历STL容器的全部或部分元素
  • STL中的每一个容器类模版中,都定义了一组对应的迭代器类。
  • 所以容器都提供两种迭代器
    • Container::iterator以“读写”模式遍历元素
    • Container::const_iterator以“只读”模式遍历元素
  • 由end操作返回的迭代指向vector的“末端元素的下一个”。通常称为超出末端迭代器(off-the-end iterator)。
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
#include <vector>
using namespace std; void print_array(const vector<int> & array)
{
vector<int>::const_iterator iter; cout << "array["; for (iter = array.begin(); iter != array.end(); ++ iter)
cout << *iter << " , "; cout << "]\n" << endl;
} int main()
{
vector<int> array;
vector<int>::iterator iter;
vector<int>::const_iterator citer; array.push_back();
array.push_back();
array.push_back(); array.pop_back(); iter = array.begin(); cout << *iter << "\n" << endl; *iter = ; citer = array.begin(); cout << *citer << "\n" << endl; // *citer = 20; // Error : Cannot assign to return value because function 'operator*' returns a const value print_array(array); return ;
}

vector_iterator.cpp


array[ ,  , ]

Program ended with exit code: 

View Result

  • STL中的排序算法sort模版有两种:一种以升序排列,另一种用自定义的_compare函数代替operator<(x,y)。
  • 下例演示了sort函数的使用。
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
#include <vector>
using namespace std; bool compare(const int & a, const int & b)
{
return a > b;
} void print_array(const vector<int> & array)
{
vector<int>::const_iterator iter; cout << "array["; for (iter = array.begin(); iter != array.end(); ++ iter)
cout << *iter << " , "; cout << "]\n" << endl;
} int main()
{
vector<int> array;
vector<int>::iterator iter;
vector<int>::const_iterator citer; array.push_back();
array.push_back();
array.push_back(); print_array(array); sort(array.begin(), array.end()); cout << "After sort(array.begin(), array.end())\n" << endl; print_array(array); sort(array.begin(), array.end(), compare); cout << "After sort(array.begin(), array.end(), compare)\n" << endl; print_array(array); return ;
}

sort_int.cpp

array[ ,  ,  , ]

After sort(array.begin(), array.end())

array[ ,  ,  , ]

After sort(array.begin(), array.end(), compare)

array[ ,  ,  , ]

Program ended with exit code: 

View Result

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
#include <vector>
#include <string>
using namespace std; class ID
{
public:
ID() : m_name(""), m_score() {}
ID(string name, int score) : m_name(name), m_score(score){} string m_name;
int m_score;
}; bool operator== (const ID & x, const ID & y)
{
return (x.m_name == y.m_name) && (x.m_score == y.m_score);
} bool operator< (const ID & x, const ID & y)
{
return x.m_score < y.m_score;
} bool compare(const ID & x, const ID & y)
{
return x.m_score > y.m_score;
} int main()
{
vector<ID> ids;
vector<ID>::iterator iter; ids.push_back(ID("Tom", ));
ids.push_back(ID("John", ));
ids.push_back(ID("Alex", )); // By default
sort(ids.begin(), ids.end()); cout << "After sort(ids.begin(), ids.end())\n" << endl; for (iter = ids.begin(); iter != ids.end(); ++ iter)
{
cout << (*iter).m_name << " : " << (*iter).m_score << endl;
} cout << endl; // With compare function
sort(ids.begin(), ids.end(), compare); cout << "After sort(ids.begin(), ids.end(), compare)\n" << endl; for (iter = ids.begin(); iter != ids.end(); ++ iter)
{
cout << (*iter).m_name << " : " << (*iter).m_score << endl;
} return ;
}

sort_class.cpp

After sort(ids.begin(), ids.end())

John :
Alex :
Tom : After sort(ids.begin(), ids.end(), compare) Tom :
Alex :
John :
Program ended with exit code:

View Result

  • 列表list使用双向链表管理元素
  • List的元素可以是任意类型,但必须具备赋值和拷贝能力
  • List不支持随机存取,不提供下表操作符
  • 在任何位置上指向元素的插入和删除效率高
  • list - C++ Reference
  • list是顺序访问的容器
  • list也有insert函数,它不会使任何迭代子或引用变得无效。
  • 链式结构最大的优点是序列便于重组,插入或删除链表中对象时无需移动其他对象的存储位置。
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
#include <list>
using namespace std; bool compare(const int & a, const int & b)
{
return a > b;
} void show_list(const list<int> & coll)
{
list<int>::const_iterator citer; for (citer = coll.begin(); citer != coll.end(); ++ citer)
cout << *citer << endl;
} int main()
{
list<int> coll; coll.push_back(); //
coll.push_back(); // 10, 11
coll.push_front(); // 12, 10, 11
coll.push_front(); // 9, 12, 10, 11 list<int>::iterator iter = coll.begin(); // coll.erase(iter); // 12, 10, 11 iter ++; // coll.erase(iter); // 10, 11 coll.push_back(); // 10, 11, 2
coll.push_back(); // 10, 11, 2, 1 // Sort in ascending order
coll.sort(); cout << "After coll.sort() : \n" << endl; show_list(coll); cout << endl; // Sort in descending order
coll.sort(compare); cout << "After coll.sort(compare) : \n" << endl; show_list(coll); return ;
}

list.cpp

After coll.sort() : 

After coll.sort(compare) : 

Program ended with exit code: 

View Result

  • 集合set与映射map是两种主要的非线性容器类
    • 内部实现一般为平衡二叉树(balanced binary tree)
  • set - C++ Reference
    • http://www.cplusplus.com/reference/set/set/
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
#include <set>
using namespace std; int main()
{
set<int> coll;
set<int>::iterator iter; coll.insert();
coll.insert();
coll.insert();
coll.insert(-);
coll.insert(); for (iter = coll.begin(); iter != coll.end(); ++ iter) {
cout << *iter << endl;
} return ;
}

set.cpp

-

Program ended with exit code: 

View Result

  • map是STL的一个关联容器,它提供一对一的数据处理能力,为键值对。
    • 其中第一个为关键字key,每个key只能在map中出现一次
    • 第二个为该关键字的值value
  • map - C++ Reference
    • http://www.cplusplus.com/reference/map/map/
  • map数据的插入
    • 用insert函数插入pair数据
    • 用insert函数插入value_type数据
    • 用insert函数插入make_pair数据
      • make_pair - C++ Reference
        • http://www.cplusplus.com/reference/utility/make_pair/
    • 直接用下标访问赋值
  • map的大小:size()函数
  • 数据的遍历:迭代器iterator
  • unordered_map内部实现使用hash表,效率更高
 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include <iostream>
#include <map>
#include <string>
using namespace std; class Compare
{
public:
// Overload operator(), must be wtih "const"
bool operator() (const int & a, const int & b) const
{
return a > b;
}
}; int main()
{
map<int, string> coll;
map<int, string>::iterator iter; coll.insert(pair<int, string>(, "Nine"));
coll.insert(map<int, string>::value_type(, "One"));
coll.insert(make_pair(, "Two"));
coll[] = "Zero"; cout << "Sort in default ascending order\n" << endl; for (iter = coll.begin(); iter != coll.end(); ++ iter)
cout << iter->first << " : " << iter->second << endl; cout << endl; // Sort with overloaded Compare
map<int, string, Compare> coll_cmp;
map<int, string, Compare>::iterator iter_cmp; coll_cmp.insert(pair<int, string>(, "Nine"));
coll_cmp.insert(map<int, string>::value_type(, "One"));
coll_cmp.insert(make_pair(, "Two"));
coll_cmp[] = "Zero"; cout << "Sort in descending order with overloaded Compare operator\n" << endl; for (iter_cmp = coll_cmp.begin(); iter_cmp != coll_cmp.end(); ++ iter_cmp)
cout << iter_cmp->first << " : " << iter_cmp->second << endl; return ;
}

map.cpp

Sort in default ascending order

 : Zero
: One
: Two
: Nine Sort in descending order with overloaded Compare operator : Nine
: Two
: One
: Zero
Program ended with exit code:

View Result

智能指针

  • 使用类模版来管理指针,创建模版类管理指针,实现资源的管理。
  • 下例实现了智能指针。注意它不是线程安全的。
  • #pragma once_百度百科
    • https://baike.baidu.com/item/%23pragma%20once
    • #pragma once是编译器相关的,有的编译器支持,有的编译器不支持,具体情况请查看编译器API文档,不过现在大部分编译器都有这个杂注了。
    • #ifndef,#define,#endif是C/C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C++语言的编译器上都是有效的,如果写的程序要跨平台,最好使用这种方式。
 //
// RefCount.h
// LeetCode
//
// Created by Hao on 2018/1/14.
// Copyright © 2018年 Hao. All rights reserved.
// #ifndef RefCount_h
#define RefCount_h //#pragma once #include <stdio.h> #define TRACE printf class RefCount
{
public:
RefCount(void)
{
TRACE("RefCount constructing\n\n");
crefs = ;
} virtual ~RefCount(void)
{
TRACE("RefCount destructing\n\n");
} void upcount(void)
{
++ crefs;
TRACE("up to %d\n\n", crefs);
} void downcount(void)
{
if (-- crefs == ) {
TRACE("down to %d\n\n", crefs);
delete this;
} else {
TRACE("down to %d\n\n", crefs);
}
} private:
int crefs;
}; class Sample : public RefCount
{
public:
Sample()
{
TRACE("Sample constructing\n\n");
} ~Sample()
{
TRACE("Sample destructing\n\n");
} void doSomething(void)
{
printf("Did something\n\n");
}
}; #endif /* RefCount_h */

RefCount.h

 //
// SmartPtr.h
// LeetCode
//
// Created by Hao on 2018/1/14.
// Copyright © 2018年 Hao. All rights reserved.
// #ifndef SmartPtr_h
#define SmartPtr_h #include <stdio.h> #define TRACE printf /*
//Compiler switch
#ifdef TRACE_SMPTR
#define TRACE printf
#else
#define TRACE
#endif
*/ template <typename T>
class SmartPtr
{
public:
SmartPtr(T* p_) : m_p(p_)
{
TRACE("SmartPtr constructing\n\n");
m_p->upcount();
} ~SmartPtr(void)
{
TRACE("SmartPtr destructing\n\n");
m_p->downcount();
} operator T* (void)
{
TRACE("%s, %d\n\n", __func__, __LINE__);
return m_p;
} T& operator* (void)
{
TRACE("%s, %d\n\n", __func__, __LINE__);
return *m_p;
} T* operator-> (void)
{
TRACE("%s, %d\n\n", __func__, __LINE__);
return m_p;
} SmartPtr& operator= (SmartPtr<T> &p_)
{
TRACE("%s, %d\n\n", __func__, __LINE__);
return operator= ((T*)p_);
} SmartPtr& operator= (T* p_)
{
TRACE("%s, %d\n\n", __func__, __LINE__);
p_->upcount();
m_p->downcount();
m_p = p_; return *this;
} SmartPtr(const SmartPtr<T> &p_)
{
TRACE("%s, %d\n\n", __func__, __LINE__);
m_p = p_.m_p;
m_p->upcount();
} private:
T* m_p;
}; #endif /* SmartPtr_h */

SmartPtr.h

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "RefCount.h"
#include "SmartPtr.h"
#include <iostream>
using namespace std; int main(int argc, char* argv[])
{
cout << "SmartPtr<Sample> p = new Sample; --->\n" << endl; SmartPtr<Sample> p = new Sample; // Sample* cout << "SmartPtr<Sample> p2 = new Sample; --->\n" << endl; SmartPtr<Sample> p2 = new Sample; cout << "p = p2; --->\n" << endl; p = p2; cout << "p->doSomething(); --->\n" << endl; p->doSomething(); cout << "(*p).doSomething(); --->\n" << endl; (*p).doSomething(); return ;
}

main.cpp

SmartPtr<Sample> p = new Sample; --->

RefCount constructing

Sample constructing

SmartPtr constructing

up to 

SmartPtr<Sample> p2 = new Sample; --->

RefCount constructing

Sample constructing

SmartPtr constructing

up to 

p = p2; --->

operator=, 

operator Sample *, 

operator=, 

up to 

down to 

Sample destructing

RefCount destructing

p->doSomething(); --->

operator->, 

Did something

(*p).doSomething(); --->

operator*, 

Did something

SmartPtr destructing

down to 

SmartPtr destructing

down to 

Sample destructing

RefCount destructing

Program ended with exit code: 

View Result

设计模式

设计模式简介

  • 设计模式实际上讨论的是在解决面向对象设计的某类问题时,应该设计哪些类,这些类之间如何通信。
  • 设计模式可以帮助设计者更快更好的完成系统设计。

单例设计模式

  • 保证在应用程序中,一个类只有一个对象
  • 将构造函数设置为私有,在类的实现中确保生成对象的个数
  • 下例演示了singleton的实现,需要把构造函数/析构函数/拷贝构造函数/赋值运算符重载都声明为私有,再使用public函数来控制类的构造和析构,从而实现一个类只有一个对象
 //
// Singleton.hpp
// LeetCode
//
// Created by Hao on 2017/12/27.
// Copyright © 2017年 Hao. All rights reserved.
// #ifndef Singleton_hpp
#define Singleton_hpp class Singleton
{
public:
static Singleton* getInstance();
void doSomething();
void destroy(); private:
Singleton();
~Singleton();
Singleton(const Singleton &);
Singleton& operator=(const Singleton &); static Singleton *instance;
}; #endif /* Singleton_hpp */

Singleton.hpp

 //
// Singleton.cpp
// LeetCode
//
// Created by Hao on 2017/12/27.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Singleton.hpp" #include <iostream>
using namespace std; // 静态成员变量需要在类外初始化
Singleton* Singleton::instance = nullptr; Singleton::Singleton()
{
cout << "Constructing Singleton instance --->\n" << endl;
} Singleton::~Singleton()
{
cout << "Destructing Singleton instance --->\n" << endl;
} // Not thread safe,
// Need to use pthread_mutex_lock/unlock
Singleton* Singleton::getInstance()
{
Singleton* ret = instance; if (instance == nullptr) {
instance = new Singleton();
ret = instance;
} return ret;
} void Singleton::doSomething()
{
cout << __func__ << " , LINE " << __LINE__ << "\n" << endl;
} void Singleton::destroy()
{
delete this;
instance = nullptr;
}

Singleton.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Singleton.hpp" #include <iostream>
using namespace std; int main ()
{
/*
Singleton* val = new Singleton(); // ERROR : Calling a private constructor of class 'Singleton'
delete val; // ERROR : Calling a private destructor of class 'Singleton'
val = nullptr;
*/ Singleton::getInstance()->doSomething(); Singleton::getInstance()->destroy(); return ;
}

main.cpp

Constructing Singleton instance --->

doSomething , LINE 

Destructing Singleton instance --->

Program ended with exit code: 

View Result

简单工厂模式

  • 使用简单工厂模式实现开闭原则
  • 简单工厂模式_百度百科
    • https://baike.baidu.com/item/简单工厂模式
    • 简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
    • 简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。
    • 该模式中包含的角色及其职责
      • 工厂(Creator)角色:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
      • 抽象产品(Product)角色:简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
      • 具体产品(Concrete Product)角色:是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
    • 使用场景
      • 工厂类负责创建的对象比较少;
      • 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;
      • 由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。
  • 下例演示了简单工厂模式应用。注意接口类纯虚函数的析构函数需要定义,类的前置声明。
 //
// Fruit.hpp
// LeetCode
//
// Created by Hao on 2018/1/5.
// Copyright © 2018年 Hao. All rights reserved.
// #ifndef Fruit_hpp
#define Fruit_hpp #include <iostream>
using namespace std; class Fruit
{
public:
virtual ~Fruit() = ; // 接口类纯虚函数的析构函数需要定义
virtual void plant() = ;
virtual void grow() = ;
virtual void harvest() = ;
}; class Apple : public Fruit
{
public:
Apple();
~Apple(); void plant();
void grow();
void harvest();
}; class Grape : public Fruit
{
public:
Grape();
~Grape(); void plant();
void grow();
void harvest();
}; enum {
APPLE = ,
GRAPE = ,
ORANGE =
}; // 前置声明
class Orange; class Gardener
{
public:
Gardener();
~Gardener(); // 0 apple, 1 grape, orange
Fruit * getFruit(int); private:
Apple * m_apple;
Grape * m_grape;
Orange * m_orange;
}; class Orange : public Fruit
{
public:
Orange();
~Orange(); void plant();
void grow();
void harvest();
}; #endif /* Fruit_hpp */

Fruit.hpp

 //
// Fruit.cpp
// LeetCode
//
// Created by Hao on 2018/1/5.
// Copyright © 2018年 Hao. All rights reserved.
// #include "Fruit.hpp"
#include <iostream>
using namespace std; Fruit::~Fruit()
{
cout << "Fruit destructing\n" << endl;
} Apple::Apple()
{
cout << "Apple constructing\n" << endl;
} Apple::~Apple()
{
cout << "Apple destructing\n" << endl;
} void Apple::plant()
{
cout << "Apple planting\n" << endl;
} void Apple::grow()
{
cout << "Apple growing\n" << endl;
} void Apple::harvest()
{
cout << "Apple harvesting\n" << endl;
} Grape::Grape()
{
cout << "Grape constructing\n" << endl;
} Grape::~Grape()
{
cout << "Grape destructing\n" << endl;
} void Grape::plant()
{
cout << "Grape planting\n" << endl;
} void Grape::grow()
{
cout << "Grape growing\n" << endl;
} void Grape::harvest()
{
cout << "Grape harvesting\n" << endl;
} Orange::Orange()
{
cout << "Orange constructing\n" << endl;
} Orange::~Orange()
{
cout << "Orange destructing\n" << endl;
} void Orange::plant()
{
cout << "Orange planting\n" << endl;
} void Orange::grow()
{
cout << "Orange growing\n" << endl;
} void Orange::harvest()
{
cout << "Orange harvesting\n" << endl;
} Gardener::Gardener()
{
cout << "Gardener constructing\n" << endl; m_apple = nullptr;
m_grape = nullptr;
m_orange = nullptr;
} Gardener::~Gardener()
{
cout << "Gardener destructing\n" << endl; if (m_apple != nullptr) {
delete m_apple;
m_apple = nullptr;
} if (m_grape != nullptr) {
delete m_grape;
m_grape = nullptr;
} if (m_orange != nullptr) {
delete m_orange;
m_orange = nullptr;
}
} Fruit * Gardener::getFruit(int type)
{
Fruit * fruit = nullptr; if (APPLE == type) {
if (nullptr == m_apple)
m_apple = new Apple(); fruit = m_apple;
} else if (GRAPE == type) {
if (nullptr == m_grape)
m_grape = new Grape(); fruit = m_grape;
} else if (ORANGE == type) {
if (nullptr == m_orange)
m_orange = new Orange(); fruit = m_orange;
} return fruit;
}

Fruit.cpp

 //
// main.cpp
// LeetCode
//
// Created by Hao on 2017/3/16.
// Copyright © 2017年 Hao. All rights reserved.
// #include "Fruit.hpp" int main ()
{
Gardener Tom; Fruit * fruit = Tom.getFruit(APPLE); fruit->plant();
fruit->grow();
fruit->harvest(); cout << "------New request for ORANGE------\n" << endl; fruit = Tom.getFruit(ORANGE); fruit->plant();
fruit->grow();
fruit->harvest(); return ;
}

main.cpp

Gardener constructing

Apple constructing

Apple planting

Apple growing

Apple harvesting

------New request for ORANGE------

Orange constructing

Orange planting

Orange growing

Orange harvesting

Gardener destructing

Apple destructing

Fruit destructing

Orange destructing

Fruit destructing

Program ended with exit code: 

View Result