编译过程分为四个过程:编译(编译预处理、编译、优化),汇编,链接。
编译预处理:处理以 # 开头的指令;
编译、优化:将源码 .cpp 文件翻译成 .s 汇编代码;
汇编:将汇编代码 .s 翻译成机器指令 .o 文件;
链接:汇编程序生成的目标文件,即 .o 文件,并不会立即执行,因为可能会出现:.cpp 文件中的函数引用了另一个 .cpp文件中定义的符号或者调用了某个库文件中的函数。那链接的目的就是将这些文件对应的目标文件连接成一个整体,从而生成可执行的程序 .exe文件。
链接分为两种:
静态链接:代码从其所在的静态链接库中拷贝到最终的可执行程序中,在该程序被执行时,这些代码会被装入到该进程的虚拟地址空间中。
动态链接:代码被放到动态链接库或共享对象的某个目标文件中,链接程序只是在最终的可执行程序中记录了共享对象的名字等一些信息。在程序执行时,动态链接库的全部内容会被映射到运行时相应进行的虚拟地址的空间。
二者的优缺点:
静态链接:浪费空间,每个可执行程序都会有目标文件的一个副本,这样如果目标文件进行了更新操作,就需要重新进行编译链接生成可执行程序(更新困难);优点就是执行的时候运行速度快,因为可执行程序具备了程序运行的所有内容。
动态链接:节省内存、更新方便,但是动态链接是在程序运行时,每次执行都需要链接,相比静态链接会有一定的性能损失。
C++ 内存分区:栈、堆、全局/静态存储区、常量存储区、代码区。
全局变量、局部变量、静态全局变量、静态局部变量的区别:
C++ 变量根据定义的位置的不同的生命周期,具有不同的作用域,作用域可分为 6 种:全局作用域,局部作用域,语句作用域,类作用域,命名空间作用域和文件作用域。
从作用域看:
从分配内存空间看:
如果在头文件中定义全局变量,当该头文件被多个文件 include 时,该头文件中的全局变量就会被定义多次,导致重复定义,因此不能再头文件中定义全局变量。
什么是内存对齐?内存对齐的原则?为什么要进行内存对齐,有什么优点?
内存对齐:编译器将程序中的每个“数据单元”安排在字的整数倍的地址指向的内存之中
内存对齐的原则:
进行内存对齐的原因:(主要是硬件设备方面的问题)
内存对齐的优点:
内存泄漏:由于疏忽或错误导致的程序未能释放已经不再使用的内存。
进一步解释:
char *p = (char *)malloc(10);
char *p1 = (char *)malloc(10);
p = np;
开始时,指针 p 和 p1 分别指向一块内存空间,但指针 p 被重新赋值,导致 p 初始时指向的那块内存空间无法找到,从而发生了内存泄漏。
防止内存泄漏的方法:
内存泄漏检测工具的实现原理:
内存检测工具有很多,这里重点介绍下 valgrind 。
智能指针是为了解决动态内存分配时带来的内存泄漏以及多次释放同一块内存空间而提出的。C++11 中封装在了 < memory > 头文件中。
C++11 中智能指针包括以下三种:
共享指针(shared_ptr):资源可以被多个指针共享,使用计数机制表明资源被几个指针共享。通过 use_count() 查看资源的所有者的个数,可以通过 unique_ptr、weak_ptr 来构造,调用 release() 释放资源的所有权,计数减一,当计数减为 0 时,会自动释放内存空间,从而避免了内存泄漏。
独占指针(unique_ptr):独享所有权的智能指针,资源只能被一个指针占有,该指针不能拷贝构造和赋值。但可以进行移动构造和移动赋值构造(调用move() 函数),即一个 unique_ptr 对象赋值给另一个 unique_ptr 对象,可以通过该方法进行赋值。
弱指针(weak_ptr):指向 shared_ptr 指向的对象,能够解决由shared_ptr带来的循环引用问题。
智能指针的实现原理: 计数原理。
#include
#include
template <typename T>
class SmartPtr
{
private :
T *_ptr;
size_t *_count;
public:
SmartPtr(T *ptr = nullptr) : _ptr(ptr)
{
if (_ptr)
{
_count = new size_t(1);
}
else
{
_count = new size_t(0);
}
}
~SmartPtr()
{
(*this->_count)--;
if (*this->_count == 0)
{
delete this->_ptr;
delete this->_count;
}
}
SmartPtr(const SmartPtr &ptr) // 拷贝构造:计数 +1
{
if (this != &ptr)
{
this->_ptr = ptr._ptr;
this->_count = ptr._count;
(*this->_count)++;
}
}
SmartPtr &operator=(const SmartPtr &ptr) // 赋值运算符重载
{
if (this->_ptr == ptr._ptr)
{
return *this;
}
if (this->_ptr) // 将当前的 ptr 指向的原来的空间的计数 -1
{
(*this->_count)--;
if (this->_count == 0)
{
delete this->_ptr;
delete this->_count;
}
}
this->_ptr = ptr._ptr;
this->_count = ptr._count;
(*this->_count)++; // 此时 ptr 指向了新赋值的空间,该空间的计数 +1
return *this;
}
T &operator*()
{
assert(this->_ptr == nullptr);
return *(this->_ptr);
}
T *operator->()
{
assert(this->_ptr == nullptr);
return this->_ptr;
}
size_t use_count()
{
return *this->count;
}
};
借助 std::move() 可以实现将一个 unique_ptr 对象赋值给另一个 unique_ptr 对象,其目的是实现所有权的转移。
// A 作为一个类
std::unique_ptr<A> ptr1(new A());
std::unique_ptr<A> ptr2 = std::move(ptr1);
智能指针可能出现的问题:循环引用
在如下例子中定义了两个类 Parent、Child,在两个类中分别定义另一个类的对象的共享指针,由于在程序结束后,两个指针相互指向对方的内存空间,导致内存无法释放。
#include
#include
using namespace std;
class Child;
class Parent;
class Parent {
private:
shared_ptr<Child> ChildPtr;
public:
void setChild(shared_ptr<Child> child) {
this->ChildPtr = child;
}
void doSomething() {
if (this->ChildPtr.use_count()) {
}
}
~Parent() {
}
};
class Child {
private:
shared_ptr<Parent> ParentPtr;
public:
void setPartent(shared_ptr<Parent> parent) {
this->ParentPtr = parent;
}
void doSomething() {
if (this->ParentPtr.use_count()) {
}
}
~Child() {
}
};
int main() {
weak_ptr<Parent> wpp;
weak_ptr<Child> wpc;
{
shared_ptr<Parent> p(new Parent);
shared_ptr<Child> c(new Child);
p->setChild(c);
c->setPartent(p);
wpp = p;
wpc = c;
cout << p.use_count() << endl; // 2
cout << c.use_count() << endl; // 2
}
cout << wpp.use_count() << endl; // 1
cout << wpc.use_count() << endl; // 1
return 0;
}
循环引用的解决方法: weak_ptr
循环引用:该被调用的析构函数没有被调用,从而出现了内存泄漏。
#include
#include
using namespace std;
class Child;
class Parent;
class Parent {
private:
//shared_ptr ChildPtr;
weak_ptr<Child> ChildPtr;
public:
void setChild(shared_ptr<Child> child) {
this->ChildPtr = child;
}
void doSomething() {
//new shared_ptr
if (this->ChildPtr.lock()) {
}
}
~Parent() {
}
};
class Child {
private:
shared_ptr<Parent> ParentPtr;
public:
void setPartent(shared_ptr<Parent> parent) {
this->ParentPtr = parent;
}
void doSomething() {
if (this->ParentPtr.use_count()) {
}
}
~Child() {
}
};
int main() {
weak_ptr<Parent> wpp;
weak_ptr<Child> wpc;
{
shared_ptr<Parent> p(new Parent);
shared_ptr<Child> c(new Child);
p->setChild(c);
c->setPartent(p);
wpp = p;
wpc = c;
cout << p.use_count() << endl; // 2
cout << c.use_count() << endl; // 1
}
cout << wpp.use_count() << endl; // 0
cout << wpc.use_count() << endl; // 0
return 0;
}
1. auto 类型推导
auto 关键字:自动类型推导,编译器会在 编译期间 通过初始值推导出变量的类型,通过 auto 定义的变量必须有初始值。
auto 关键字基本的使用语法如下:
2. decltype 类型推导
decltype 关键字:decltype 是“declare type”的缩写,译为“声明类型”。和 auto 的功能一样,都用来在编译时期进行自动类型推导。如果希望从表达式中推断出要定义的变量的类型,但是不想用该表达式的值初始化变量,这时就不能再用 auto。decltype 作用是选择并返回操作数的数据类型。
区别:
auto var = val1 + val2;
decltype(val1 + val2) var1 = 0;
3. lambda 表达式
lambda 表达式,又被称为 lambda 函数或者 lambda 匿名函数。
lambda匿名函数的定义:
[capture list] (parameter list) -> return type
{
function body;
};
其中:
#include
#include
using namespace std;
int main()
{
int arr[4] = {4, 2, 3, 1};
//对 a 数组中的元素进行升序排序
sort(arr, arr+4, [=](int x, int y) -> bool{ return x < y; } );
for(int n : arr){
cout << n << " ";
}
return 0;
}
4. 范围 for 语句
for (declaration : expression){
statement
}
参数的含义:
5. 右值引用
右值引用:绑定到右值的引用,用 && 来获得右值引用,右值引用只能绑定到要销毁的对象。为了和右值引用区分开,常规的引用称为左值引用。
#include
#include
using namespace std;
int main()
{
int var = 42;
int &l_var = var;
int &&r_var = var; // error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int' 错误:不能将右值引用绑定到左值上
int &&r_var2 = var + 40; // 正确:将 r_var2 绑定到求和结果上
return 0;
}
6. 标准库 move() 函数
move() 函数:通过该函数可获得绑定到左值上的右值引用,该函数包括在 utility 头文件中。该知识点会在后续的章节中做详细的说明。
7. 智能指针
相关知识已在第一章中进行了详细的说明,这里不再重复。
8. delete 函数和 default 函数
#include
using namespace std;
class A
{
public:
A() = default; // 表示使用默认的构造函数
~A() = default; // 表示使用默认的析构函数
A(const A &) = delete; // 表示类的对象禁止拷贝构造
A &operator=(const A &) = delete; // 表示类的对象禁止拷贝赋值
};
int main()
{
A ex1;
A ex2 = ex1; // error: use of deleted function 'A::A(const A&)'
A ex3;
ex3 = ex1; // error: use of deleted function 'A& A::operator=(const A&)'
return 0;
}
首先说一下面向对象和面向过程:
区别和联系:
区别:
面向对象:对象是指具体的某一个事物,这些事物的抽象就是类,类中包含数据(成员变量)和动作(成员方法)。
面向对象的三大特性:
class A
{
public:
void fun(int tmp);
void fun(float tmp); // 重载 参数类型不同(相对于上一个函数)
void fun(int tmp, float tmp1); // 重载 参数个数不同(相对于上一个函数)
void fun(float tmp, int tmp1); // 重载 参数顺序不同(相对于上一个函数)
int fun(int tmp); // error: 'int A::fun(int)' cannot be overloaded 错误:注意重载不关心函数返回类型
};
#include
using namespace std;
class Base
{
public:
void fun(int tmp, float tmp1) { cout << "Base::fun(int tmp, float tmp1)" << endl; }
};
class Derive : public Base
{
public:
void fun(int tmp) { cout << "Derive::fun(int tmp)" << endl; } // 隐藏基类中的同名函数
};
int main()
{
Derive ex;
ex.fun(1); // Derive::fun(int tmp)
ex.fun(1, 0.01); // error: candidate expects 1 argument, 2 provided
return 0;
}
说明:上述代码中 ex.fun(1, 0.01); 出现错误,说明派生类中将基类的同名函数隐藏了。若是想调用基类中的同名函数,可以加上类型名指明 ex.Base::fun(1, 0.01);,这样就可以调用基类中的同名函数。
#include
using namespace std;
class Base
{
public:
virtual void fun(int tmp) { cout << "Base::fun(int tmp) : " << tmp << endl; }
};
class Derived : public Base
{
public:
virtual void fun(int tmp) { cout << "Derived::fun(int tmp) : " << tmp << endl; } // 重写基类中的 fun 函数
};
int main()
{
Base *p = new Derived();
p->fun(3); // Derived::fun(int) : 3
return 0;
}
重写和重载的区别:
隐藏和重写,重载的区别:
说明:该问题最好结合自己的项目经历进行展开解释,或举一些恰当的例子,同时对比下面向过程编程。
面向对象编程进一步说明:
面向对象编程将数据成员和成员函数封装到一个类中,并声明数据成员和成员函数的访问级别(public、private、protected),以便控制类对象对数据成员和函数的访问,对数据成员起到一定的保护作用。而且在类的对象调用成员函数时,只需知道成员函数的名、参数列表以及返回值类型即可,无需了解其函数的实现原理。当类内部的数据成员或者成员函数发生改变时,不影响类外部的代码。
多态:多态就是不同继承类的对象,对同一消息做出不同的响应,基类的指针指向或绑定到派生类的对象,使得基类指针呈现不同的表现方式。在基类的函数前加上 virtual 关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。
实现方法:多态是通过虚函数实现的,虚函数的地址保存在虚函数表中,虚函数表的地址保存在含有虚函数的类的实例对象的内存空间中。
实现过程:
#include
using namespace std;
class Base
{
public:
virtual void fun() { cout << "Base::fun()" << endl; }
virtual void fun1() { cout << "Base::fun1()" << endl; }
virtual void fun2() { cout << "Base::fun2()" << endl; }
};
class Derive : public Base
{
public:
void fun() { cout << "Derive::fun()" << endl; }
virtual void D_fun1() { cout << "Derive::D_fun1()" << endl; }
virtual void D_fun2() { cout << "Derive::D_fun2()" << endl; }
};
int main()
{
Base *p = new Derive();
p->fun(); // Derive::fun() 调用派生类中的虚函数
return 0;
}
基类的虚函数表如下:
派生类的对象虚函数表如下:
简单解释:当基类的指针指向派生类的对象时,通过派生类的对象的虚表指针找到虚函数表(派生类的对象虚函数表),进而找到相应的虚函数 Derive::f() 进行调用。
strlen 源代码:
size_t strlen(const char *str) {
size_t length = 0;
while (*str++)
++length;
return length;
}
#include
#include
using namespace std;
int main()
{
char arr[10] = "hello";
cout << strlen(arr) << endl; // 5
cout << sizeof(arr) << endl; // 10
return 0;
}
#include
#include
using namespace std;
void size_of(char arr[])
{
cout << sizeof(arr) << endl; // warning: 'sizeof' on array function parameter 'arr' will return size of 'char*' .
cout << strlen(arr) << endl;
}
int main()
{
char arr[20] = "hello";
size_of(arr);
return 0;
}
/*
输出结果:
8
5
*/
strlen 本身是库函数,因此在程序运行过程中,计算长度;而 sizeof 在编译时,计算长度;
sizeof 的参数可以是类型,也可以是变量;strlen 的参数必须是 char* 类型的变量。
lambda 表达式的定义形式如下:
[capture list] (parameter list) -> reurn type
{
function body
}
其中:
举例:
lambda 表达式常搭配排序算法使用。
#include
#include
#include
using namespace std;
int main()
{
vector<int> arr = {3, 4, 76, 12, 54, 90, 34};
sort(arr.begin(), arr.end(), [](int a, int b) { return a > b; }); // 降序排序
for (auto a : arr)
{
cout << a << " ";
}
return 0;
}
/*
运行结果:90 76 54 34 12 4 3
*/
作用:用来声明类构造函数是显示调用的,而非隐式调用,可以阻止调用构造函数时进行隐式转换。只可用于修饰单参构造函数,因为无参构造函数和多参构造函数本身就是显示调用的,再加上 explicit 关键字也没有什么意义。
隐式转换:
#include
#include
using namespace std;
class A
{
public:
int var;
A(int tmp)
{
var = tmp;
}
};
int main()
{
A ex = 10; // 发生了隐式转换
return 0;
}
上述代码中,A ex = 10; 在编译时,进行了隐式转换,将 10 转换成 A 类型的对象,然后将该对象赋值给 ex,等同于如下操作:
为了避免隐式转换,可用 explicit 关键字进行声明:
#include
#include
using namespace std;
class A
{
public:
int var;
explicit A(int tmp)
{
var = tmp;
cout << var << endl;
}
};
int main()
{
A ex(100);
A ex1 = 10; // error: conversion from 'int' to non-scalar type 'A' requested
return 0;
}
作用:
static 定义静态变量,静态函数。
#include
using namespace std;
int fun(){
static int var = 1; // var 只在第一次进入这个函数的时初始化
var += 1;
return var;
}
int main()
{
for(int i = 0; i < 10; ++i)
cout << fun() << " "; // 2 3 4 5 6 7 8 9 10 11
return 0;
}
#include
using namespace std;
class A
{
private:
int var;
static int s_var; // 静态成员变量
public:
void show()
{
cout << s_var++ << endl;
}
static void s_show()
{
cout << s_var << endl;
// cout << var << endl; // error: invalid use of member 'A::a' in static member function. 静态成员函数不能调用非静态成员变量。无法使用 this.var
// show(); // error: cannot call member function 'void A::show()' without object. 静态成员函数不能调用非静态成员函数。无法使用 this.show()
}
};
int A::s_var = 1; // 静态成员变量在类外进行初始化赋值,默认初始化为 0
int main()
{
// cout << A::sa << endl; // error: 'int A::sa' is private within this context
A ex;
ex.show();
A::s_show();
}
static 静态成员变量:
#include
using namespace std;
class A
{
public:
static int s_var;
int var;
void fun1(int i = s_var); // 正确,静态成员变量可以作为成员函数的参数
void fun2(int i = var); // error: invalid use of non-static data member 'A::var'
};
int main()
{
return 0;
}
#include
using namespace std;
class A
{
public:
static A s_var; // 正确,静态数据成员
A var; // error: field 'var' has incomplete type 'A'
A *p; // 正确,指针
A &var1; // 正确,引用
};
int main()
{
return 0;
}
static 静态成员函数:
相同点:
不同点:
作用:
在类中的用法:
const 成员变量:
const 成员函数:
区别:
const 的优点:
#include
#define INTPTR1 int *
typedef int * INTPTR2;
using namespace std;
int main()
{
INTPTR1 p1, p2; // p1: int *; p2: int
INTPTR2 p3, p4; // p3: int *; p4: int *
int var = 1;
const INTPTR1 p5 = &var; // 相当于 const int * p5; 常量指针,即不可以通过 p5 去修改 p5 指向的内容,但是 p5 可以指向其他内容。
const INTPTR2 p6 = &var; // 相当于 int * const p6; 指针常量,不可使 p6 再指向其他内容。
return 0;
}
#include
#define MAX(X, Y) ((X)>(Y)?(X):(Y))
#define MIN(X, Y) ((X)<(Y)?(X):(Y))
using namespace std;
int main ()
{
int var1 = 10, var2 = 100;
cout << MAX(var1, var2) << endl;
cout << MIN(var1, var2) << endl;
return 0;
}
/*
程序运行结果:
100
10
*/
作用:
inline 是一个关键字,可以用于定义内联函数。内联函数,像普通函数一样被调用,但是在调用时并不通过函数调用的机制而是直接在调用点处展开,这样可以大大减少由函数调用带来的开销,从而提高程序的运行效率。
使用方法:
#include
using namespace std;
class A{
public:
int var;
A(int tmp){
var = tmp;
}
void fun(){
cout << var << endl;
}
};
int main()
{
return 0;
}
#include
using namespace std;
class A{
public:
int var;
A(int tmp){
var = tmp;
}
void fun();
};
inline void A::fun(){
cout << var << endl;
}
int main()
{
return 0;
}
另外,可以在声明函数和定义函数的同时加上 inline;也可以只在函数声明时加 inline,而定义函数时不加 inline。只要确保在调用该函数之前把 inline 的信息告知编译器即可。
#include
#define MAX(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
inline int fun_max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int var = 1;
cout << MAX(var, 5) << endl;
cout << fun_max(var, 0) << endl;
return 0;
}
/*
程序运行结果:
5
1
*/
new 是 C++ 中的关键字,用来动态分配内存空间,实现方式如下:
int *p = new int[5];
delete 的实现原理:
delete 和 delete [] 的区别:
在使用的时候 new、delete 搭配使用,malloc、free 搭配使用。
malloc 的原理:
malloc 的底层实现:
brk() 函数实现原理:向高地址的方向移动指向数据段的高地址的指针 _enddata。
mmap 内存映射原理:
1.进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域;
2.调用内核空间的系统调用函数 mmap(),实现文件物理地址和进程虚拟地址的一一映射关系;
3.进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝。
说明:union 是联合体,struct 是结构体。
区别:
#include
using namespace std;
typedef union
{
char c[10];
char cc1; // char 1 字节,按该类型的倍数分配大小
} u11;
typedef union
{
char c[10];
int i; // int 4 字节,按该类型的倍数分配大小
} u22;
typedef union
{
char c[10];
double d; // double 8 字节,按该类型的倍数分配大小
} u33;
typedef struct s1
{
char c; // 1 字节
double d; // 1(char)+ 7(内存对齐)+ 8(double)= 16 字节
} s11;
typedef struct s2
{
char c; // 1 字节
char cc; // 1(char)+ 1(char)= 2 字节
double d; // 2 + 6(内存对齐)+ 8(double)= 16 字节
} s22;
typedef struct s3
{
char c; // 1 字节
double d; // 1(char)+ 7(内存对齐)+ 8(double)= 16 字节
char cc; // 16 + 1(char)+ 7(内存对齐)= 24 字节
} s33;
int main()
{
cout << sizeof(u11) << endl; // 10
cout << sizeof(u22) << endl; // 12
cout << sizeof(u33) << endl; // 16
cout << sizeof(s11) << endl; // 16
cout << sizeof(s22) << endl; // 16
cout << sizeof(s33) << endl; // 24
cout << sizeof(int) << endl; // 4
cout << sizeof(double) << endl; // 8
return 0;
}
struct A{};
class B : A{}; // private 继承
struct C : B{}; // public 继承
举例:
#include
using namespace std;
class A{
public:
void funA(){
cout << "class A" << endl;
}
};
struct B: A{ // 由于 B 是 struct,A 的默认继承级别为 public
public:
void funB(){
cout << "class B" << endl;
}
};
class C: B{ // 由于 C 是 class,B 的默认继承级别为 private,所以无法访问基类 B 中的 printB 函数
};
int main(){
A ex1;
ex1.funA(); // class A
B ex2;
ex2.funA(); // class A
ex2.funB(); // class B
C ex3;
ex3.funB(); // error: 'B' is not an accessible base of 'C'.
return 0;
}
volatile 的作用:当对象的值可能在程序的控制或检测之外被改变时,应该将该对象声明为 violatile,告知编译器不应对这样的对象进行优化。
volatile不具有原子性。
volatile 对编译器的影响:使用该关键字后,编译器不会对相应的对象进行优化,即不会将变量从内存缓存到寄存器中,防止多个线程有可能使用内存中的变量,有可能使用寄存器中的变量,从而导致程序错误。
使用 volatile 关键字的场景:
volatile 关键字和 const 关键字可以同时使用,某种类型可以既是 volatile 又是 const ,同时具有二者的属性。
#include
using namespace std;
int * fun(int tmp){
static int var = 10;
var *= tmp;
return &var;
}
int main() {
cout << *fun(5) << endl;
return 0;
}
/*
运行结果:
50
*/
说明:上述代码中在函数 fun 中定义了静态局部变量 var,使得离开该函数的作用域后,该变量不会销毁,返回到主函数中,该变量依然存在,从而使程序得到正确的运行结果。但是,该静态局部变量直到程序运行结束后才销毁,浪费内存空间。
当 C++ 程序 需要调用 C 语言编写的函数,C++ 使用链接指示,即 extern “C” 指出任意非 C++ 函数所用的语言。
举例:
// 可能出现在 C++ 头文件中的链接指示
extern "C"{
int strcmp(const char*, const char*);
}
C 语言代码:
#include
void main(){
printf("%d\n", sizeof(1==1));
}
/*
运行结果:
4
*/
C++ 代码:
#include
using namespace std;
int main() {
cout << sizeof(1==1) << endl;
return 0;
}
/*
1
*/
void *memcpy(void *dst, const void *src, size_t size)
{
char *psrc;
char *pdst;
if (NULL == dst || NULL == src)
{
return NULL;
}
if ((src < dst) && (char *)src + size > (char *)dst) // 出现地址重叠的情况,自后向前拷贝
{
psrc = (char *)src + size - 1;
pdst = (char *)dst + size - 1;
while (size--)
{
*pdst-- = *psrc--;
}
}
else
{
psrc = (char *)src;
pdst = (char *)dst;
while (size--)
{
*pdst++ = *psrc++;
}
}
return dst;
}
strcpy 函数的缺陷:strcpy 函数不检查目的缓冲区的大小边界,而是将源字符串逐一的全部赋值给目的字符串地址起始的一块连续的内存空间,同时加上字符串终止符,会导致其他变量被覆盖。
#include
#include
using namespace std;
int main()
{
int var = 0x11112222;
char arr[10];
cout << "Address : var " << &var << endl;
cout << "Address : arr " << &arr << endl;
strcpy(arr, "hello world!");
cout << "var:" << hex << var << endl; // 将变量 var 以 16 进制输出
cout << "arr:" << arr << endl;
return 0;
}
/*
Address : var 0x23fe4c
Address : arr 0x23fe42
var:11002164
arr:hello world!
*/
说明:从上述代码中可以看出,变量 var 的后六位被字符串 “hello world!” 的 “d!\0” 这三个字符改变,这三个字符对应的 ascii 码的十六进制为:\0(0x00),!(0x21),d(0x64)。
原因:变量 arr 只分配的 10 个内存空间,通过上述程序中的地址可以看出 arr 和 var 在内存中是连续存放的,但是在调用 strcpy 函数进行拷贝时,源字符串 “hello world!” 所占的内存空间为 13,因此在拷贝的过程中会占用 var 的内存空间,导致 var的后六位被覆盖。
auto 类型推导的原理:
编译器根据初始值来推算变量的类型,要求用 auto 定义变量时必须有初始值。编译器推断出来的 auto 类型有时和初始值类型并不完全一样,编译器会适当改变结果类型使其更符合初始化规则。
虚函数:被 virtual 关键字修饰的成员函数,就是虚函数。
#include
using namespace std;
class A
{
public:
virtual void v_fun() // 虚函数
{
cout << "A::v_fun()" << endl;
}
};
class B : public A
{
public:
void v_fun()
{
cout << "B::v_fun()" << endl;
}
};
int main()
{
A *p = new B();
p->v_fun(); // B::v_fun()
return 0;
}
纯虚函数:
说明:
实现机制:虚函数通过虚函数表来实现。虚函数的地址保存在虚函数表中,在类的对象所在的内存空间中,保存了指向虚函数表的指针(称为“虚表指针”),通过虚表指针可以找到类对应的虚函数表。虚函数表解决了基类和派生类的继承问题和类中成员函数的覆盖问题,当用基类的指针来操作一个派生类的时候,这张虚函数表就指明了实际应该调用的函数
虚函数表相关知识点:
注:虚函数表和类绑定,虚表指针和对象绑定。即类的不同的对象的虚函数表是一样的,但是每个对象都有自己的虚表指针,来指向类的虚函数表。
实例:
无虚函数覆盖的情况:
#include
using namespace std;
class Base
{
public:
virtual void B_fun1() { cout << "Base::B_fun1()" << endl; }
virtual void B_fun2() { cout << "Base::B_fun2()" << endl; }
virtual void B_fun3() { cout << "Base::B_fun3()" << endl; }
};
class Derive : public Base
{
public:
virtual void D_fun1() { cout << "Derive::D_fun1()" << endl; }
virtual void D_fun2() { cout << "Derive::D_fun2()" << endl; }
virtual void D_fun3() { cout << "Derive::D_fun3()" << endl; }
};
int main()
{
Base *p = new Derive();
p->B_fun1(); // Base::B_fun1()
return 0;
}
基类和派生类的继承关系:
基类的虚函数表:
派生类的虚函数表:
主函数中基类的指针 p 指向了派生类的对象,当调用函数 B_fun1() 时,通过派生类的虚函数表找到该函数的地址,从而完成调用。
编译器处理虚函数表:
#include
using namespace std;
class Base
{
public:
virtual void B_fun1() { cout << "Base::B_fun1()" << endl; }
virtual void B_fun2() { cout << "Base::B_fun2()" << endl; }
virtual void B_fun3() { cout << "Base::B_fun3()" << endl; }
};
class Derive : public Base
{
public:
virtual void D_fun1() { cout << "Derive::D_fun1()" << endl; }
virtual void D_fun2() { cout << "Derive::D_fun2()" << endl; }
virtual void D_fun3() { cout << "Derive::D_fun3()" << endl; }
};
int main()
{
Base *p = new Derive();
p->B_fun1(); // Base::B_fun1()
return 0;
}
基类和派生类的继承关系:
基类的虚函数表:
派生类的虚函数表:
2. 单继承有虚函数覆盖的情况:
#include
using namespace std;
class Base
{
public:
virtual void fun1() { cout << "Base::fun1()" << endl; }
virtual void B_fun2() { cout << "Base::B_fun2()" << endl; }
virtual void B_fun3() { cout << "Base::B_fun3()" << endl; }
};
class Derive : public Base
{
public:
virtual void fun1() { cout << "Derive::fun1()" << endl; }
virtual void D_fun2() { cout << "Derive::D_fun2()" << endl; }
virtual void D_fun3() { cout << "Derive::D_fun3()" << endl; }
};
int main()
{
Base *p = new Derive();
p->fun1(); // Derive::fun1()
return 0;
}
#include
using namespace std;
class Base1
{
public:
virtual void B1_fun1() { cout << "Base1::B1_fun1()" << endl; }
virtual void B1_fun2() { cout << "Base1::B1_fun2()" << endl; }
virtual void B1_fun3() { cout << "Base1::B1_fun3()" << endl; }
};
class Base2
{
public:
virtual void B2_fun1() { cout << "Base2::B2_fun1()" << endl; }
virtual void B2_fun2() { cout << "Base2::B2_fun2()" << endl; }
virtual void B2_fun3() { cout << "Base2::B2_fun3()" << endl; }
};
class Base3
{
public:
virtual void B3_fun1() { cout << "Base3::B3_fun1()" << endl; }
virtual void B3_fun2() { cout << "Base3::B3_fun2()" << endl; }
virtual void B3_fun3() { cout << "Base3::B3_fun3()" << endl; }
};
class Derive : public Base1, public Base2, public Base3
{
public:
virtual void D_fun1() { cout << "Derive::D_fun1()" << endl; }
virtual void D_fun2() { cout << "Derive::D_fun2()" << endl; }
virtual void D_fun3() { cout << "Derive::D_fun3()" << endl; }
};
int main(){
Base1 *p = new Derive();
p->B1_fun1(); // Base1::B1_fun1()
return 0;
}
基类和派生类的关系:
派生类的虚函数表:(基类的顺序和声明的顺序一致)
4. 多继承有虚函数覆盖的情况:
#include
using namespace std;
class Base1
{
public:
virtual void fun1() { cout << "Base1::fun1()" << endl; }
virtual void B1_fun2() { cout << "Base1::B1_fun2()" << endl; }
virtual void B1_fun3() { cout << "Base1::B1_fun3()" << endl; }
};
class Base2
{
public:
virtual void fun1() { cout << "Base2::fun1()" << endl; }
virtual void B2_fun2() { cout << "Base2::B2_fun2()" << endl; }
virtual void B2_fun3() { cout << "Base2::B2_fun3()" << endl; }
};
class Base3
{
public:
virtual void fun1() { cout << "Base3::fun1()" << endl; }
virtual void B3_fun2() { cout << "Base3::B3_fun2()" << endl; }
virtual void B3_fun3() { cout << "Base3::B3_fun3()" << endl; }
};
class Derive : public Base1, public Base2, public Base3
{
public:
virtual void fun1() { cout << "Derive::fun1()" << endl; }
virtual void D_fun2() { cout << "Derive::D_fun2()" << endl; }
virtual void D_fun3() { cout << "Derive::D_fun3()" << endl; }
};
int main(){
Base1 *p1 = new Derive();
Base2 *p2 = new Derive();
Base3 *p3 = new Derive();
p1->fun1(); // Derive::fun1()
p2->fun1(); // Derive::fun1()
p3->fun1(); // Derive::fun1()
return 0;
}
左值:指表达式结束后依然存在的持久对象。
右值:表达式结束就不再存在的临时对象。
左值和右值的区别:左值持久,右值短暂
右值引用和左值引用的区别:
std::move 可以将一个左值强制转化为右值,继而可以通过右值引用使用该值,以用于移动语义。
#include
using namespace std;
void fun1(int& tmp)
{
cout << "fun1(int& tmp):" << tmp << endl;
}
void fun2(int&& tmp)
{
cout << "fun2(int&& tmp)" << tmp << endl;
}
int main()
{
int var = 11;
fun1(12); // error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
fun1(var);
fun2(1);
}
std::move() 函数原型:
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type &&>(t);
}
说明:引用折叠原理
小结:通过引用折叠原理可以知道,move() 函数的形参既可以是左值也可以是右值。
remove_reference 具体实现:
//原始的,最通用的版本
template <typename T> struct remove_reference{
typedef T type; //定义 T 的类型别名为 type
};
//部分版本特例化,将用于左值引用和右值引用
template <class T> struct remove_reference<T&> //左值引用
{ typedef T type; }
template <class T> struct remove_reference<T&&> //右值引用
{ typedef T type; }
//举例如下,下列定义的a、b、c三个变量都是int类型
int i;
remove_refrence<decltype(42)>::type a; //使用原版本,
remove_refrence<decltype(i)>::type b; //左值引用特例版本
remove_refrence<decltype(std::move(i))>::type b; //右值引用特例版本
举例:
int var = 10;
转化过程:
1. std::move(var) => std::move(int&& &) => 折叠后 std::move(int&)
2. 此时:T 的类型为 int&,typename remove_reference<T>::type 为 int,这里使用 remove_reference 的左值引用的特例化版本
3. 通过 static_cast 将 int& 强制转换为 int&&
整个std::move被实例化如下
string&& move(int& t)
{
return static_cast<int&&>(t);
}
总结:
std::move() 实现原理:
指针: 指向另外一种类型的复合类型。
指针的大小: 在 64 位计算机中,指针占 8 个字节空间。
#include
using namespace std;
int main(){
int *p = nullptr;
cout << sizeof(p) << endl; // 8
char *p1 = nullptr;
cout << sizeof(p1) << endl; // 8
return 0;
}
指针的用法:
#include
using namespace std;
class A
{
};
int main()
{
A *p = new A();
return 0;
}
#include
using namespace std;
int main(void)
{
const int c_var = 10;
const int * p = &c_var;
cout << *p << endl;
return 0;
}
#include
using namespace std;
int add(int a, int b){
return a + b;
}
int main(void)
{
int (*fun_p)(int, int);
fun_p = add;
cout << fun_p(1, 6) << endl;
return 0;
}
#include
using namespace std;
class A
{
public:
int var1, var2;
int add(){
return var1 + var2;
}
};
int main()
{
A ex;
ex.var1 = 3;
ex.var2 = 4;
int *p = &ex.var1; // 指向对象成员变量的指针
cout << *p << endl;
int (A::*fun_p)();
fun_p = A::add; // 指向对象成员函数的指针 fun_p
cout << (ex.*fun_p)() << endl;
return 0;
}
#include
#include
using namespace std;
class A
{
public:
void set_name(string tmp)
{
this->name = tmp;
}
void set_age(int tmp)
{
this->age = age;
}
void set_sex(int tmp)
{
this->sex = tmp;
}
void show()
{
cout << "Name: " << this->name << endl;
cout << "Age: " << this->age << endl;
cout << "Sex: " << this->sex << endl;
}
private:
string name;
int age;
int sex;
};
int main()
{
A *p = new A();
p->set_name("Alice");
p->set_age(16);
p->set_sex(1);
p->show();
return 0;
}
若指针指向一块内存空间,当这块内存空间被释放后,该指针依然指向这块内存空间,此时,称该指针为“悬空指针”。
void *p = malloc(size);
free(p);
// 此时,p 指向的内存空间已释放, p 就是悬空指针。
野指针:
“野指针”是指不确定其指向的指针,未初始化的指针为“野指针”。
void *p;
// 此时 p 是“野指针”。
nullptr 的优势:
#include
#include
using namespace std;
void fun(char const *p)
{
cout << "fun(char const *p)" << endl;
}
void fun(int tmp)
{
cout << "fun(int tmp)" << endl;
}
int main()
{
fun(nullptr); // fun(char const *p)
/*
fun(NULL); // error: call of overloaded 'fun(NULL)' is ambiguous
*/
return 0;
}
常量指针:
常量指针本质上是个指针,只不过这个指针指向的对象是常量。
特点:const 的位置在指针声明运算符 * 的左侧。只要 const 位于 * 的左侧,无论它在类型名的左边或右边,都表示指向常量的指针。(可以这样理解,* 左侧表示指针指向的对象,该对象为常量,那么该指针为常量指针。)
const int * p;
int const * p;
注意 1:指针指向的对象不能通过这个指针来修改,也就是说常量指针可以被赋值为变量的地址,之所以叫做常量指针,是限制了通过这个指针修改变量的值。
#include
using namespace std;
int main()
{
const int c_var = 8;
const int *p = &c_var;
*p = 6; // error: assignment of read-only location '* p'
return 0;
}
注意 2:虽然常量指针指向的对象不能变化,可是因为常量指针本身是一个变量,因此,可以被重新赋值。
例如:
#include
using namespace std;
int main()
{
const int c_var1 = 8;
const int c_var2 = 8;
const int *p = &c_var1;
p = &c_var2;
return 0;
}
指针常量:
指针常量的本质上是个常量,只不过这个常量的值是一个指针。
特点:const 位于指针声明操作符右侧,表明该对象本身是一个常量,* 左侧表示该指针指向的类型,即以 * 为分界线,其左侧表示指针指向的类型,右侧表示指针本身的性质。
const int var;
int * const c_p = &var;
注意 1:指针常量的值是指针,这个值因为是常量,所以指针本身不能改变。
#include
using namespace std;
int main()
{
int var, var1;
int * const c_p = &var;
c_p = &var1; // error: assignment of read-only variable 'c_p'
return 0;
}
注意 2:指针的内容可以改变。
#include
using namespace std;
int main()
{
int var = 3;
int * const c_p = &var;
*c_p = 12;
return 0;
}
指针函数:
指针函数本质是一个函数,只不过该函数的返回值是一个指针。相对于普通函数而言,只是返回值是指针。
#include
using namespace std;
struct Type
{
int var1;
int var2;
};
Type * fun(int tmp1, int tmp2){
Type * t = new Type();
t->var1 = tmp1;
t->var2 = tmp2;
return t;
}
int main()
{
Type *p = fun(5, 6);
return 0;
}
函数指针:
函数指针本质是一个指针变量,只不过这个指针指向一个函数。函数指针即指向函数的指针。
举例:
#include
using namespace std;
int fun1(int tmp1, int tmp2)
{
return tmp1 * tmp2;
}
int fun2(int tmp1, int tmp2)
{
return tmp1 / tmp2;
}
int main()
{
int (*fun)(int x, int y);
fun = fun1;
cout << fun(15, 5) << endl;
fun = fun2;
cout << fun(15, 5) << endl;
return 0;
}
/*
运行结果:
75
3
*/
函数指针和指针函数的区别:
#include
#include
using namespace std;
class Base
{
};
class Derive : public Base
{
};
int main()
{
Base *p1 = new Derive();
Derive *p2 = new Derive();
//向上类型转换
p1 = dynamic_cast<Base *>(p2);
if (p1 == NULL)
{
cout << "NULL" << endl;
}
else
{
cout << "NOT NULL" << endl; //输出
}
return 0;
}
4.在下行转换时,基类的指针类型转化为派生类类的指针类型,只有当要转换的指针指向的对象类型和转化以后的对象类型相同时,才会转化成功。
#include
#include
using namespace std;
class Base
{
public:
virtual void fun()
{
cout << "Base::fun()" << endl;
}
};
class Derive : public Base
{
public:
virtual void fun()
{
cout << "Derive::fun()" << endl;
}
};
int main()
{
Base *p1 = new Derive();
Base *p2 = new Base();
Derive *p3 = new Derive();
//转换成功
p3 = dynamic_cast<Derive *>(p1);
if (p3 == NULL)
{
cout << "NULL" << endl;
}
else
{
cout << "NOT NULL" << endl; // 输出
}
//转换失败
p3 = dynamic_cast<Derive *>(p2);
if (p3 == NULL)
{
cout << "NULL" << endl; // 输出
}
else
{
cout << "NOT NULL" << endl;
}
return 0;
}
需要重载操作符 == 判断两个结构体是否相等,不能用函数 memcmp 来判断两个结构体是否相等,因为 memcmp 函数是逐个字节进行比较的,而结构体存在内存空间中保存时存在字节对齐,字节对齐时补的字节内容是随机的,会产生垃圾值,所以无法比较。
利用运算符重载来实现结构体对象的比较:
#include
using namespace std;
struct A
{
char c;
int val;
A(char c_tmp, int tmp) : c(c_tmp), val(tmp) {}
friend bool operator==(const A &tmp1, const A &tmp2); // 友元运算符重载函数
};
bool operator==(const A &tmp1, const A &tmp2)
{
return (tmp1.c == tmp2.c && tmp1.val == tmp2.val);
}
int main()
{
A ex1('a', 90), ex2('b', 80);
if (ex1 == ex2)
cout << "ex1 == ex2" << endl;
else
cout << "ex1 != ex2" << endl; // 输出
return 0;
}
参数传递的三种方式:
#include
using namespace std;
void fun1(int tmp){ // 值传递
cout << &tmp << endl;
}
void fun2(int * tmp){ // 指针传递
cout << tmp << endl;
}
void fun3(int &tmp){ // 引用传递
cout << &tmp << endl;
}
int main()
{
int var = 5;
cout << "var 在主函数中的地址:" << &var << endl;
cout << "var 值传递时的地址:";
fun1(var);
cout << "var 指针传递时的地址:";
fun2(&var);
cout << "var 引用传递时的地址:";
fun3(var);
return 0;
}
/*
运行结果:
var 在主函数中的地址:0x23fe4c
var 值传递时的地址:0x23fe20
var 指针传递时的地址:0x23fe4c
var 引用传递时的地址:0x23fe4c
*/
说明:从上述代码的运行结果可以看出,只有在值传递时,形参和实参的地址不一样,在函数体内操作的不是变量本身。引用传递和指针传递,在函数体内操作的是变量本身。
模板:创建类或者函数的蓝图或者公式,分为函数模板和类模板。
实现方式:模板定义以关键字 template 开始,后跟一个模板参数列表。
template <typename T, typename U, ...>
函数模板:通过定义一个函数模板,可以避免为每一种类型定义一个新函数。
#include
using namespace std;
template <typename T>
T add_fun(const T & tmp1, const T & tmp2){
return tmp1 + tmp2;
}
int main(){
int var1, var2;
cin >> var1 >> var2;
cout << add_fun(var1, var2);
double var3, var4;
cin >> var3 >> var4;
cout << add_fun(var3, var4);
return 0;
}
类模板:类似函数模板,类模板以关键字 template 开始,后跟模板参数列表。但是,编译器不能为类模板推断模板参数类型,需要在使用该类模板时,在模板名后面的尖括号中指明类型。
#include
using namespace std;
template <typename T>
class Complex
{
public:
//构造函数
Complex(T a, T b)
{
this->a = a;
this->b = b;
}
//运算符重载
Complex<T> operator+(Complex &c)
{
Complex<T> tmp(this->a + c.a, this->b + c.b);
cout << tmp.a << " " << tmp.b << endl;
return tmp;
}
private:
T a;
T b;
};
int main()
{
Complex<int> a(10, 20);
Complex<int> b(20, 30);
Complex<int> c = a + b;
return 0;
}
#include
using namespace std;
template <typename T>
T add_fun(const T & tmp1, const T & tmp2){
return tmp1 + tmp2;
}
int main(){
int var1, var2;
cin >> var1 >> var2;
cout << add_fun<int>(var1, var2); // 显式调用
double var3, var4;
cin >> var3 >> var4;
cout << add_fun(var3, var4); // 隐式调用
return 0;
}
可变参数模板:接受可变数目参数的模板函数或模板类。将可变数目的参数被称为参数包,包括模板参数包和函数参数包。
用省略号来指出一个模板参数或函数参数表示一个包,在模板参数列表中,class… 或 typename… 指出接下来的参数表示零个或多个类型的列表;一个类型名后面跟一个省略号表示零个或多个给定类型的非类型参数的列表。当需要知道包中有多少元素时,可以使用 sizeof… 运算符。
template <typename T, typename... Args> // Args 是模板参数包
void foo(const T &t, const Args&... rest); // 可变参数模板,rest 是函数参数包
#include
using namespace std;
template <typename T>
void print_fun(const T &t)
{
cout << t << endl; // 最后一个元素
}
template <typename T, typename... Args>
void print_fun(const T &t, const Args &...args)
{
cout << t << " ";
print_fun(args...);
}
int main()
{
print_fun("Hello", "wolrd", "!");
return 0;
}
/*运行结果:
Hello wolrd !
*/
说明:可变参数函数通常是递归的,第一个版本的 print_fun 负责终止递归并打印初始调用中的最后一个实参。第二个版本的 print_fun 是可变参数版本,打印绑定到 t 的实参,并用来调用自身来打印函数参数包中的剩余值。
模板特化的原因:模板并非对任何模板实参都合适、都能实例化,某些情况下,通用模板的定义对特定类型不合适,可能会编译失败,或者得不到正确的结果。因此,当不希望使用模板版本时,可以定义类或者函数模板的一个特例化版本。
模板特化:模板参数在某种特定类型下的具体实现。分为函数模板特化和类模板特化
特化分为全特化和偏特化:
说明:要区分下函数重载与函数模板特化
定义函数模板的特化版本,本质上是接管了编译器的工作,为原函数模板定义了一个特殊实例,而不是函数重载,函数模板特化并不影响函数匹配。
#include
#include
using namespace std;
//函数模板
template <class T>
bool compare(T t1, T t2)
{
cout << "通用版本:";
return t1 == t2;
}
template <> //函数模板特化
bool compare(char *t1, char *t2)
{
cout << "特化版本:";
return strcmp(t1, t2) == 0;
}
int main(int argc, char *argv[])
{
char arr1[] = "hello";
char arr2[] = "abc";
cout << compare(123, 123) << endl;
cout << compare(arr1, arr2) << endl;
return 0;
}
/*
运行结果:
通用版本:1
特化版本:0
*/
include<文件名> 和 #include"文件名" 的区别:
泛型编程实现的基础:模板。模板是创建类或者函数的蓝图或者说公式,当时用一个 vector 这样的泛型,或者 find 这样的泛型函数时,编译时会转化为特定的类或者函数。
泛型编程涉及到的知识点较广,例如:容器、迭代器、算法等都是泛型编程的实现实例。面试者可选择自己掌握比较扎实的一方面进行展开。
《大话设计模式》一书中提到 24 种设计模式,这 24 种设计模式没必要面面俱到,但一定要深入了解其中的几种,最好结合自己在实际开发过程中的例子进行深入的了解。
设计模式有 6 大设计原则:
设计模式分为三类:
下面介绍常见的几种设计模式:
单例模式:保证类的实例化对象仅有一个,并且提供一个访问他的全局访问点。
应用场景:
实现方式:
单例模式可以通过全局或者静态变量的形式实现,这样比较简单,但是这样会影响封装性,难以保证别的代码不会对全局变量造成影响。
不安全的实现方式:
原因:考虑当两个线程同时调用 getInstance 方法,并且同时检测到 instance 是 NULL,两个线程会同时实例化对象,不符合单例模式的要求。
class Singleton{
private:
static Singleton * instance;
Singleton(){}
Singleton(const Singleton& tmp){}
Singleton& operator=(const Singleton& tmp){}
public:
static Singleton* getInstance(){
if(instance == NULL){
instance = new Singleton();
}
return instance;
}
};
Singleton* Singleton::instance = NULL;
分类:
线程安全的懒汉模式实现:
方法:加锁
存在的问题:每次判断实例对象是否为空,都要被锁定,如果是多线程的话,就会造成大量线程阻塞。
class Singleton{
private:
static pthread_mutex_t mutex;
static Singleton * instance;
Singleton(){
pthread_mutex_init(&mutex, NULL);
}
Singleton(const Singleton& tmp){}
Singleton& operator=(const Singleton& tmp){}
public:
static Singleton* getInstance(){
pthread_mutex_lock(&mutex);
if(instance == NULL){
instance = new Singleton();
}
pthread_mutex_unlock(&mutex);
return instance;
}
};
Singleton* Singleton::instance = NULL;
pthread_mutex_t Singleton::mutex;
方法:内部静态变量,在全局访问点 getInstance 中定义静态实例。
class Singleton{
private:
static Singleton* instance;
Singleton(const Singleton& temp){}
Singleton& operator=(const Singleton& temp){}
protected:
Singleton(){}
public:
static Singleton* getInstance(){
return instance;
}
};
Singleton* Singleton::instance = new Singleton();
工厂模式:包括简单工厂模式、抽象工厂模式、工厂方法模式
应用场景:
#include
#include
using namespace std;
// Here is the product class
class Operation
{
public:
int var1, var2;
virtual double GetResult()
{
double res = 0;
return res;
}
};
class Add_Operation : public Operation
{
public:
virtual double GetResult()
{
return var1 + var2;
}
};
class Sub_Operation : public Operation
{
public:
virtual double GetResult()
{
return var1 - var2;
}
};
class Mul_Operation : public Operation
{
public:
virtual double GetResult()
{
return var1 * var2;
}
};
class Div_Operation : public Operation
{
public:
virtual double GetResult()
{
return var1 / var2;
}
};
// Here is the Factory class
class Factory
{
public:
static Operation *CreateProduct(char op)
{
switch (op)
{
case '+':
return new Add_Operation();
case '-':
return new Sub_Operation();
case '*':
return new Mul_Operation();
case '/':
return new Div_Operation();
default:
return new Add_Operation();
}
}
};
int main()
{
int a, b;
cin >> a >> b;
Operation *p = Factory::CreateProduct('+');
p->var1 = a;
p->var2 = b;
cout << p->GetResult() << endl;
p = Factory::CreateProduct('*');
p->var1 = a;
p->var2 = b;
cout << p->GetResult() << endl;
return 0;
}
修正了简单工厂模式中不遵守开放封闭原则。把选择判断移到了客户端去实现,如果想添加新功能就不用修改原来的类,直接修改客户端即可。
应用场景:
一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
一个类通过其派生类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其派生类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,派生类对象将覆盖父类对象,从而使得系统更容易扩展。
将创建对象的任务委托给多个工厂派生类中的某一个,客户端在使用时可以无须关心是哪一个工厂派生类创建产品派生类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
实现方式:
#include
#include
using namespace std;
// Here is the product class
class Operation
{
public:
int var1, var2;
virtual double GetResult()
{
double res = 0;
return res;
}
};
class Add_Operation : public Operation
{
public:
virtual double GetResult()
{
return var1 + var2;
}
};
class Sub_Operation : public Operation
{
public:
virtual double GetResult()
{
return var1 - var2;
}
};
class Mul_Operation : public Operation
{
public:
virtual double GetResult()
{
return var1 * var2;
}
};
class Div_Operation : public Operation
{
public:
virtual double GetResult()
{
return var1 / var2;
}
};
class Factory
{
public:
virtual Operation *CreateProduct() = 0;
};
class Add_Factory : public Factory
{
public:
Operation *CreateProduct()
{
return new Add_Operation();
}
};
class Sub_Factory : public Factory
{
public:
Operation *CreateProduct()
{
return new Sub_Operation();
}
};
class Mul_Factory : public Factory
{
public:
Operation *CreateProduct()
{
return new Mul_Operation();
}
};
class Div_Factory : public Factory
{
public:
Operation *CreateProduct()
{
return new Div_Operation();
}
};
int main()
{
int a, b;
cin >> a >> b;
Add_Factory *p_fac = new Add_Factory();
Operation *p_pro = p_fac->CreateProduct();
p_pro->var1 = a;
- List item
p_pro->var2 = b;
cout << p_pro->GetResult() << endl;
Mul_Factory *p_fac1 = new Mul_Factory();
Operation *p_pro1 = p_fac1->CreateProduct();
p_pro1->var1 = a;
p_pro1->var2 = b;
cout << p_pro1->GetResult() << endl;
return 0;
}
定义了一个创建一系列相关或相互依赖的接口,而无需指定他们的具体类。
应用场景:
实现方式:
#include
#include
using namespace std;
// Here is the product class
class Operation_Pos
{
public:
int var1, var2;
virtual double GetResult()
{
double res = 0;
return res;
}
};
class Add_Operation_Pos : public Operation_Pos
{
public:
virtual double GetResult()
{
return var1 + var2;
}
};
class Sub_Operation_Pos : public Operation_Pos
{
public:
virtual double GetResult()
{
return var1 - var2;
}
};
class Mul_Operation_Pos : public Operation_Pos
{
public:
virtual double GetResult()
{
return var1 * var2;
}
};
class Div_Operation_Pos : public Operation_Pos
{
public:
virtual double GetResult()
{
return var1 / var2;
}
};
/*********************************************************************************/
class Operation_Neg
{
public:
int var1, var2;
virtual double GetResult()
{
double res = 0;
return res;
}
};
class Add_Operation_Neg : public Operation_Neg
{
public:
virtual double GetResult()
{
return -(var1 + var2);
}
};
class Sub_Operation_Neg : public Operation_Neg
{
public:
virtual double GetResult()
{
return -(var1 - var2);
}
};
class Mul_Operation_Neg : public Operation_Neg
{
public:
virtual double GetResult()
{
return -(var1 * var2);
}
};
class Div_Operation_Neg : public Operation_Neg
{
public:
virtual double GetResult()
{
return -(var1 / var2);
}
};
/*****************************************************************************************************/
// Here is the Factory class
class Factory
{
public:
virtual Operation_Pos *CreateProduct_Pos() = 0;
virtual Operation_Neg *CreateProduct_Neg() = 0;
};
class Add_Factory : public Factory
{
public:
Operation_Pos *CreateProduct_Pos()
{
return new Add_Operation_Pos();
}
Operation_Neg *CreateProduct_Neg()
{
return new Add_Operation_Neg();
}
};
class Sub_Factory : public Factory
{
public:
Operation_Pos *CreateProduct_Pos()
{
return new Sub_Operation_Pos();
}
Operation_Neg *CreateProduct_Neg()
{
return new Sub_Operation_Neg();
}
};
class Mul_Factory : public Factory
{
public:
Operation_Pos *CreateProduct_Pos()
{
return new Mul_Operation_Pos();
}
Operation_Neg *CreateProduct_Neg()
{
return new Mul_Operation_Neg();
}
};
class Div_Factory : public Factory
{
public:
Operation_Pos *CreateProduct_Pos()
{
return new Div_Operation_Pos();
}
Operation_Neg *CreateProduct_Neg()
{
return new Div_Operation_Neg();
}
};
int main()
{
int a, b;
cin >> a >> b;
Add_Factory *p_fac = new Add_Factory();
Operation_Pos *p_pro = p_fac->CreateProduct_Pos();
p_pro->var1 = a;
p_pro->var2 = b;
cout << p_pro->GetResult() << endl;
Add_Factory *p_fac1 = new Add_Factory();
Operation_Neg *p_pro1 = p_fac1->CreateProduct_Neg();
p_pro1->var1 = a;
p_pro1->var2 = b;
cout << p_pro1->GetResult() << endl;
return 0;
}
观察者模式:定义一种一(被观察类)对多(观察类)的关系,让多个观察对象同时监听一个被观察对象,被观察对象状态发生变化时,会通知所有的观察对象,使他们能够更新自己的状态。
观察者模式中存在两种角色:
应用场景:
实现方式:
#include
#include
#include
using namespace std;
class Subject;
//观察者 基类 (内部实例化了被观察者的对象sub)
class Observer
{
protected:
string name;
Subject *sub;
public:
Observer(string name, Subject *sub)
{
this->name = name;
this->sub = sub;
}
virtual void update() = 0;
};
class StockObserver : public Observer
{
public:
StockObserver(string name, Subject *sub) : Observer(name, sub)
{
}
void update();
};
class NBAObserver : public Observer
{
public:
NBAObserver(string name, Subject *sub) : Observer(name, sub)
{
}
void update();
};
//被观察者 基类 (内部存放了所有的观察者对象,以便状态发生变化时,给观察者发通知)
class Subject
{
protected:
list<Observer *> observers;
public:
string action; //被观察者对象的状态
virtual void attach(Observer *) = 0;
virtual void detach(Observer *) = 0;
virtual void notify() = 0;
};
class Secretary : public Subject
{
void attach(Observer *observer)
{
observers.push_back(observer);
}
void detach(Observer *observer)
{
list<Observer *>::iterator iter = observers.begin();
while (iter != observers.end())
{
if ((*iter) == observer)
{
observers.erase(iter);
return;
}
++iter;
}
}
void notify()
{
list<Observer *>::iterator iter = observers.begin();
while (iter != observers.end())
{
(*iter)->update();
++iter;
}
}
};
void StockObserver::update()
{
cout << name << " 收到消息:" << sub->action << endl;
if (sub->action == "梁所长来了!")
{
cout << "我马上关闭股票,装做很认真工作的样子!" << endl;
}
}
void NBAObserver::update()
{
cout << name << " 收到消息:" << sub->action << endl;
if (sub->action == "梁所长来了!")
{
cout << "我马上关闭NBA,装做很认真工作的样子!" << endl;
}
}
int main()
{
Subject *dwq = new Secretary();
Observer *xs = new NBAObserver("xiaoshuai", dwq);
Observer *zy = new NBAObserver("zouyue", dwq);
Observer *lm = new StockObserver("limin", dwq);
dwq->attach(xs);
dwq->attach(zy);
dwq->attach(lm);
dwq->action = "去吃饭了!";
dwq->notify();
cout << endl;
dwq->action = "梁所长来了!";
dwq->notify();
return 0;
}