类的动态内存分配

本文详细介绍了C++中类的构造函数、复制构造函数、赋值运算符的使用及注意事项,包括静态类成员、浅拷贝与深拷贝的概念,以及如何正确实现复制构造函数和赋值运算符以避免潜在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.静态类成员:

private:
        char * str;
        int length;
        static int objectNum;    //静态数据成员,为所有的对象所共享
上例中的objectNum就是静态类成员,它是所有对象所共享的,如下图:


在上例中,创建了三个对象,内存会给每个对象都分配数据单元用来存储Str和Len,但是不会为每个对象都创建num_strings,在内存中只会创建一个num_strings,供所有对象共享。静态类数据成员的初始化不是在类的声明文件中进行的,而是在类的函数定义文件中进行的。如在StringBad.cpp文件中初始化,而不是在StringBad.h文件中初始化。

2.特殊成员函数:

●默认构造函数:如果没有定义构造函数,就会自动生成。

●默认析构函数:如果没有定义析构函数,就会自动生成。

●复制构造函数:当用一个对象初始化另外一个对象时,如果我们没有专门定义复制构造函数,则会自动生成复制构造函数,详解见下面。

●赋值运算符:当用一个对象初始化另外一个对象时,如果我们没有专门定义赋值运算符,则会自动生成赋值运算符,详解见下面。

●地址运算符

3.复制构造函数:

class StringBad
{
    private:
        char * str;
        int length;
        static int objectNum;    //静态数据成员,为所有的对象所共享
    public:
        StringBad();  //默认构造函数
        StringBad(const char * s); //构造函数
        ~StringBad();   //析构函数

        friend ostream & operator<<(ostream & os, const StringBad & sb);
};
int StringBad::objectNum = 0;    //静态数据成员

StringBad::StringBad()           //默认构造函数
{
    length = 4;
    str = new char[4];
    strcpy(str, "c++");
    objectNum++;
    cout << "Object" << objectNum << ": " << str << " is created." << endl;
}

StringBad::StringBad(const char * s)   //构造函数
{
    length = strlen(s);
    str = new char[length + 1];
    strcpy(str, s);
    objectNum++;
    cout << "Object" << objectNum << ": " << str << " is created." << endl;
}

StringBad::~StringBad()   //析构函数
{
    objectNum--;
    cout << str << " is deleted." << endl;
    delete [] str;
    cout << objectNum << " objects left." << endl;
}

ostream & operator<<(ostream & os, const StringBad & sb)   //重载<<运算符
{
    os << sb.str << endl;
    return os;
}
int main()
{
    StringBad sb1("abcde"), sb2 = sb1;
    cout << sb1;
    cout << sb2;
    cout << endl;
    return 0;
}
在上例中,定义了一个类StringBad,在类中定义了一些成员函数,然后使用sb2 = sb1,用sb1来初始化sb2,当用一个对象初始化另外一个对象时,就会调用复制构造函数,此时就调用了复制构造函数。

默认的复制构造函数逐个复制非静态成员(成员复制也成为浅拷贝),复制的是成员的值。如在上例中,其实是将sb1.length赋给sb2.length,sb1.str赋给sb2.str,而对象的str成员存储的是字符串的地址,而不是字符串本身,所以这种浅拷贝是将sb1.str和sb2.str指向同一个地方,所以这种浅拷贝容易出问题,当将sb1.str指向的内容删除时,其实也将sb2.str指向的内容也删除了,再删除sb2.str所指向的内容时就会出错。

     所以,此时我们就需要自己定义一个复制构造函数来解决这种问题,将sb1.str和sb2.str指向不同的地方,但这两个地方存储的字符串内容是一样的(也就是深拷贝)。

    浅拷贝和深拷贝的区别:浅拷贝是仅仅只拷贝字符串的地址,使两个指针指向同一个地方。深拷贝是拷贝内容到另外一个地方,使另一个指针指向拷贝内容的地方,通过深拷贝两个指针指向的地址就不同,但字符串内容仍是相同的。

浅拷贝示意图:


深拷贝示意图:


此例中我们自己定义一个复制构造函数,使得通过对象初始化,也能使两个对象的str指针指向不同的地址:

StringBad::StringBad(const StringBad & st)   //复制构造函数,用于将一个对象用于初始化另外一个对象,如object1 = object2
{
    objectNum++;
    length = st.length;
    str = new char[length + 1];
    strcpy(str, st.str);
    cout << "Object" << objectNum << ": " << str << " is created." << endl;
}
警告:如果类中包含了使用new初始化的指针成员,则我们自己应定义一个复制构造函数,以复制指向的数据,而不是复制指针,这被成为深拷贝。浅拷贝仅仅只是复制指针的值。

4.赋值运算符:

object2 = object1;
当我们使用以上语句用object1对object2进行初始化时,实际是分两步来处理:先使用复制构造函数创建一个临时对象,然后通过复制运算符将临时对象复制到新对象中。在第一步中,我们使用我们自己定义的复制构造函数来进行深拷贝;在第二步中,如果我们不自己定义赋值运算符时,我们进行的会是跟默认的复制构造函数一样的浅拷贝,所以我们在这里需要自己定义赋值运算符进行深拷贝。

这里定义的赋值运算符如下:

StringBad & StringBad::operator=(const StringBad & sb)
{
    if(this == &sb)
        return *this;
    delete [] str;
    length = sb.length;
    str = new char[length + 1];
    strcpy(str, sb.str);
    return *this;
}
5.整个项目实例代码:

文件结构:


din.h代码:

#ifndef DIN_H_INCLUDED
#define DIN_H_INCLUDED

#include <iostream>
using namespace std;

class StringBad
{
    private:
        char * str;
        int length;
        static int objectNum;    //静态数据成员,为所有的对象所共享
    public:
        StringBad();  //默认构造函数
        StringBad(const char * s); //构造函数
        ~StringBad();   //析构函数
        StringBad(const StringBad & st);  //复制构造函数,用于将一个对象用令一个对象来初始化,如object1 = object2
        StringBad & operator=(const StringBad & sb);

        friend ostream & operator<<(ostream & os, const StringBad & sb);
};


#endif // DIN_H_INCLUDED
din.cpp代码:

#include <iostream>
#include <cstring>
#include "din.h"
using namespace std;

int StringBad::objectNum = 0;    //静态数据成员

StringBad::StringBad()           //默认构造函数
{
    length = 4;
    str = new char[4];
    strcpy(str, "c++");
    objectNum++;
    cout << "Object" << objectNum << ": " << str << " is created." << endl;
}

StringBad::StringBad(const StringBad & st)   //复制构造函数,用于将一个对象用于初始化另外一个对象,如object1 = object2
{
    objectNum++;
    length = st.length;
    str = new char[length + 1];
    strcpy(str, st.str);
    cout << "Object" << objectNum << ": " << str << " is created." << endl;
}

StringBad::StringBad(const char * s)   //构造函数
{
    length = strlen(s);
    str = new char[length + 1];
    strcpy(str, s);
    objectNum++;
    cout << "Object" << objectNum << ": " << str << " is created." << endl;
}

StringBad & StringBad::operator=(const StringBad & sb)
{
    if(this == &sb)
        return *this;
    delete [] str;
    length = sb.length;
    str = new char[length + 1];
    strcpy(str, sb.str);
    return *this;
}

StringBad::~StringBad()   //析构函数
{
    objectNum--;
    cout << str << " is deleted." << endl;
    delete [] str;
    cout << objectNum << " objects left." << endl;
}

ostream & operator<<(ostream & os, const StringBad & sb)   //重载<<运算符
{
    os << sb.str << endl;
    return os;
}
main.cpp代码:

#include <iostream>
#include "din.h"
using namespace std;

int main()
{
    StringBad sb1, sb2("abcde"), sb4;
    StringBad sb3 = sb2;
    sb4 = sb2;
    cout << sb1;
    cout << sb2;
    cout << sb3;
    cout << sb4;
    cout << endl;
    return 0;
}

运行结果:




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值