1. 什么是拷贝构造函数
用c++ 创建一个空类的时候,编译器会默认为这个类创建:默认构造函数、析构函数、拷贝构造函数、赋值构造函数。
如:

class A{
public:
//默认构造函数
    A() { ... }
//析构函数  
  ~A() { ... }
//拷贝构造函数
    A(const A& rhs) { ... }
//赋值构造函数
    A& operator=(const A& rhs) { ... }
};

注意:这四个函数都是public和inline的,只有在你使用A的时候才会自动生成,而且当你显示的定义了这四个函数中的任何一个时,编译器将不会生成对应的默认函数。
2. 为什么需要拷贝构造函数?拷贝构造函数什么时候会执行?
在平常的开发中下面几种情况会执行拷贝构造函数:
(1) 对象以传值的方式传到函数体

void setData(Data data);

(2) 对象以传值的方式从函数返回

Data getData();

上面两种情况虽然可以使用引用或者指针的方式避免生成副本,但这并不适合所有的情况,因为有时我们不希望在函数里面的一些操作会影响到函数外部的变量。
(3) 对象需要另一个同类型的对象初始化

Data a;
Data b = a;
Data c = Data(b);

上面三行代码中:第一行执行构造函数,第二行和第三行执行拷贝构造函数。这里要注意后面两行,因为此时b和c对象都不存在,所以执行拷贝构造函数,而不是赋值构造函数,那这里为什么不执行构造函数呢?因为构造函数会对Data对象做初始化工作,如果setData之前对Data做了其他处理,那就失效了,所以在产生Data对象副本的时候不会执行构造函数。

3. 拷贝构造函数参数需要是const的吗?
其实拷贝构造函数不一定需要是const,比如auto_ptr,由于auto_ptr涉及到“所有权”转移问题,它在调用拷贝构造函数时, 需要把自己当前维护的指针设为NULL,同时把这根指针交给另一个auto_ptr保管。
一般情况下需要设置成const的,主要作用是:
(1) 防止修改传入的参数
(2) 能接受非const对象和const对象作为参数,没有加const只能接受非const对象参数.
比如:

A(const A &a);
const A a;
A b = a; // ok

如果这样:

A(A &a);
const A a;
A b = a; // error,会编译错误,因为变量a是const的,拷贝构造函数中a不是const的。

当然类中有可以存在两个拷贝构造函数,如:

(1)A(const A &a);
(2)A(A &a);

const A a;
A b = a; // ok, 调用(1)

A a;
A b = a; // ok, 调用(2)

不过这样没必要,定义(1)就可以了。

4. 拷贝构造函数参数为什么只能是引用?
如果把拷贝构造函数参数改成A对象:
A(A a);
在vs2012环境会直接提示错误,如果这个允许的话,会直接导致无穷递归,导致程序崩溃。
如果改成指向A对象的指针:
A(A *a);
这个其实是一个参数为指针的普通自定义构造函数,不是拷贝构造函数,可以用下面代码测试,A(A*a)函数不会执行的。
A a;
A b = a;
所以拷贝构造函数只能够接受一个同类型对象的引用作为参数。

5. 怎么禁止调用拷贝构造函数?
(1)把拷贝构造函数声明成private的:

private:
A(const A &a) {};

这样其他用户就不能复制该类类型的对象了,但是类的友元和成员仍然可以复制。
(2)把拷贝构造函数声明成private的,且不对其进行定义。

private:
A(const A &a);

这样复制该类类型的对象时会提示链接失败。

6. 关于浅拷贝和深拷贝
(1) 浅拷贝
指对象在复制的时候,只对对象的数据成员进行简单的赋值处理,默认拷贝构造函数执行的是浅拷贝。一般情况下,这样做是没有问题的,但是如果有数据成员是动态分配的内存时(堆内存,如new出来的对象),就会出问题了。如下面的代码:

class B {
public:
 B() {}

 ~B() {}

int i;
};

class A {
public:
 A(){
  _b = new B();
 };

 ~A() {
  if(_b != NULL) {
   cout << "delete _b" << endl;
   delete _b;
   _b = NULL;
  }
 }

 B *_b;
};

int main(void) {
 A a;
 A b(a);

 return 0;
}

程序会发生crash,因为对象a的_b成员和对象b的_b成员指的是同一个对象,指向同一块内存区域,a执行析构函数的时候把B对象释放掉了,所以b执行析构函数的时候调用delete _b时就会释放已经释放了的内存,导致crash。
(2)深拷贝
深拷贝需要自定义拷贝构造函数,对动态成员重新分配内存空间,上面的代码修改成下面这样就ok了:

class B {
public:
 B() {}

 ~B() {}

 int i;
};

class A {
public:
 A(){
  _b = new B();
 };

 A(const A &a){
  _b = new B();
  _b->i = a._b->i;
 }

 ~A() {
  if(_b != NULL) {
   cout << "delete _b" << endl;
   delete _b;
   _b = NULL;
  }
 }

 B *_b;
};

int main(void) {
 A a;
 A b(a);

 return 0;
}

lua面向对象模拟简介

lua不是面向对象语言,但可以通过表(table)和元表(metatable)来模拟。table 是 lua 中唯一的一种数据结构,它可以用来描述原始的数组、符号表、集合、 记录、...

阅读全文

在c/c++中调用lua函数

上篇文章完成了在lua中调用c/c++函数,现在来实现在c/c++中调用lua函数。 首先完成lua代码,创建sum.lua: function add(x, y) return x + y; end 为了...

阅读全文

在lua中调用c/c++函数

lua是一种轻量级的脚本语言,用来扩展c和c++非常好,在游戏开发中使用很普遍。 首先下载lua,因为我是在win7下,所以我这里下载了luaforwindows,安装到F:\Lu...

阅读全文

1 条评论

  1. 学习BB10开发,无意中找到这里,顺便问问微信开发的怎么样了,好久没更新了应该

欢迎留言