构造函数等注意事项
- 构造函数三种调用方式
class Person
{
public:
Person(int a)
{
m_a = a;
}
Person(int a, string str)
{
m_a = a;
m_str = str;
}
Person(const Person &p)
{
p.m_a=m_a;
p.m_str=m_str;
}
private:
int m_a;
string m_str;
};
void test01()
{
//括号调用构造函数
Person p1(1);
Person p2(1, "hello");
//匿名构造函数调用
Person p3 = Person(1);
Person p4 = Person(1, "hello");
//隐式调用
Person p5 = 1;
//Person p6 = 1, "hello";错误示范,
}
- 如果写了有参构造函数,编译器不再提供无参构造函数
如果写了构造拷贝函数,编译器不在提供其他构造函数
编译器默认自动提供无参构造函数(空实现),无参析构函数(空实现),拷贝函数(拷贝类里面所有属性值) - 拷贝构造函数的调用方式
//参考上面的类
void test02()
{
//同构造函数一样,三种调用方式,
Person p_copy(p1);
Person p_copy3 = Person(p1);
Person p_copy2 = p1;
}
深拷贝和浅拷贝的问题
我们常见的用等号进行赋值的操作就是浅拷贝,而深拷贝则需要在堆区开辟内存,再进行拷贝
class Person
{
public:
Person(int* w, int w2)
{
m_w=w;
m_w2=w2;
}
~Person()
{
if(m_w != NULL)
{
delete m_w;
m_w = NULL;
}
}
private:
int* m_w;
int m_w2;
}
//当函数结束时,调用析构函数,释放内存,但是需要注意,由于两个对象里面的的指针都
//指向了堆开辟的同一块内存,所以当编译器先释放对象p2,此时就会解放堆里面的内存,
//此时再释放p1对象,调用构造函数时,由于堆里的内存已经释放,此时就会报错了,这就
//是浅拷贝带来的重复释放问题
void test03()
{
int* m = new int(10);
Person p1(m,1);
Person p2(p1);
}
//使用深拷贝就可以解决上面的问题,重写拷贝构造函数如下:
```cpp
Person(const Person &p)
{
//重新在堆里开内存,并且将需要拷贝的目标对象指向的堆内存的值放入此对象新开辟的堆内存中
//这就完成了深拷贝,虽然重新开辟堆内存后,对象里面的指针地址不同,但是地址指向的内存的值相同
//从而避免重复释放内存的问题
m_w = new int(*p.m_w);
m_w2 = p.m_w2;
}
初始化列表
//比构造函数更简洁
Person(int* w, int w2) :m_w(w), m_w2(w2) {}//写在类对象里面的函数
//在使用的时候与构造函数相同
Person p1(new int(10),1);//在类外部调用的,构造函数也是这么调用的
需要注意的一点是,初始化列表可以直接对该类中的类对象成员的成员属性进行初始化
class Tiger
{
public:
Tiger(string name)
{
m_name = name;
}
private:
string m_name;
}
class animal
{
public:
//我们知道当使用初始化列表时,相当于给属性进行赋值操作,但是对于对象来说,
//是没有赋值操作的,要么用构造函数,要么用拷贝函数,对于m_t(t)更相当于Tiger m_t = t;
//是不是很像调用构造函数的第三种方法(见上文),所以对于对象使用初始化列表,
//本质是调用构造函数,或者拷贝函数。
animal(int num,Tiger t):m_num(num),m_t(t){}
private:
int m_num;
Tiger m_t;
}