什么是桥接模式?举个例子:平时我们我们的USB(Universal Serial Bus(通用串行总线)),其实其设计的模式就是桥接模式。桥接模式的作用就是:将抽象部份与它的实现部份分离,使它们都可以独立地变化。也就是说无论USB插的是U盘,还是小风扇。都只是外部的独立变化,电脑内部是不受影响。不扯远了,这里不是主要说桥接模式的用法。下面看看使用桥接模式需要注意一个小小的问题。http://blog.csdn.net/yitouhan/article/details/41443221
#ifndef BRIDGE_H
#define BRIDGE_H
#include <stdlib.h>
class CPU
{
public:
CPU()
{
m_name = (char *)malloc(32);
};
~CPU()
{
free(m_name);
};
private:
char *m_name;
};
class Computer
{
public:
Computer(){};
~Computer(){};
CPU &GetCPU1(){ return m_cpu; }
CPU *GetCPU2(){ return &m_cpu; }
private:
CPU m_cpu;
};
int main()
{
{
Computer computer;
{
CPU cpu = computer.GetCPU1(); // 运行时报错
}
}
{
Computer computer;
{
CPU *cpu = computer.GetCPU2();
}
}
return 0;
}
#endif
当运行上面的代码时,应该会产生一个错误,因为第一个例子是有误的。因为这涉及到浅拷贝(复制)和深拷贝。首先,我们知道如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数;而这个默认拷贝构造函数就是浅拷贝。
而在C++中,在用一个对象初始化另一个对象时,只复制了成员,并没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制。也就是说CPU cpu = computer.GetCPU1();中的cpu跟computer中的m_cpu的m_name都是指同一个地址。所以当cpu生命周期结束时,cpu会调用析构函数;当computer生命周期结束时,m_cpu又会调用析构函数,从而导致析构函数会被调用再次,结合上面的例子,也就是m_name会被free了两次。因此会造成程序运行时错误。
当然,如果要解决这个问题,就是自定义拷贝构造函数(深拷贝)。
#ifndef BRIDGE_H
#define BRIDGE_H
#include <stdlib.h>
#include <string.h>
class CPU
{
static const int NAME_LENGTH = 32;
public:
CPU()
{
m_name = (char *)malloc(NAME_LENGTH);
};
~CPU()
{
free(m_name);
};
CPU(CPU &cpu)
{
m_name = (char *)malloc(NAME_LENGTH);
memcpy(m_name, cpu.GetName(), NAME_LENGTH);
}
char *GetName(){ return m_name; }
private:
char *m_name;
};
class Computer
{
public:
Computer(){};
~Computer(){};
CPU &GetCPU1(){ return m_cpu; }
CPU *GetCPU2(){ return &m_cpu; }
private:
CPU m_cpu;
};
int main()
{
{
Computer computer;
{
CPU cpu = computer.GetCPU1();
}
}
{
Computer computer;
{
CPU *cpu = computer.GetCPU2();
}
}
return 0;
}
#endif
经过这样一修改,没错,代码是正确运行。但是实际应用,上述做法是不科学的。
很明显,实际上我是想获取Computer里面的CPU信息,而不是额外拷贝出一份信息出来。下面朝着这个方向去修改。
#ifndef BRIDGE_H
#define BRIDGE_H
#include <stdlib.h>
#include <string.h>
class CPU
{
static const int NAME_LENGTH = 32;
public:
CPU()
{
m_name = (char *)malloc(NAME_LENGTH);
};
~CPU()
{
free(m_name);
};
CPU(CPU &cpu);
// {
// m_name = (char *)malloc(NAME_LENGTH);
// memcpy(m_name, cpu.GetName(), NAME_LENGTH);
// }
//
// char *GetName(){ return m_name; }
private:
char *m_name;
};
class Computer
{
public:
Computer(){};
~Computer(){};
CPU &GetCPU1(){ return m_cpu; }
CPU *GetCPU2(){ return &m_cpu; }
private:
CPU m_cpu;
};
int main()
{
{
Computer computer;
{
CPU cpu = computer.GetCPU1(); // 编译时报错
}
}
{
Computer computer;
{
CPU *cpu = computer.GetCPU2();
}
}
return 0;
}
#endif
这段代码,只是注释了一段代码,其它并没有修改。但是只要编译这段的时候,编译就会无情报错。很明显是因为我只声明了拷贝构造函数,没有定义就直接引用它。这种做法可以避免写出这样的拷贝操作(CPU cpu = computer.GetCPU1();)。这种操作的不科学之处之前已经说明。当我们要调用CPU的函数时,我们只能computer.GetCPU1().XXX();这样操作。
有一行代码由始至终我都没有提及过的就是CPU *cpu = computer.GetCPU2();。这行代码在3个例子中都是正确的,都是能达到我的目的的。但是其也有容易出错的地方,那就是很容易被delete。
其实:
CPU &GetCPU1(){ return m_cpu; }
CPU *GetCPU2(){ return &m_cpu; }
没有那一种是绝对好,至于选择哪一种,可以根据自己的需求决定!这里只是说说使用它们时要注意的问题。