什么样的类需要编写拷贝构造函数

本文详细阐述了拷贝构造函数的作用及其使用场景,特别针对含有指针成员变量的情况进行了深入分析,并介绍了如何通过拷贝构造函数实现深拷贝,避免资源泄露的问题。以string类为例,强调了自定义拷贝构造函数的重要性,特别是当类中包含需要深拷贝的字符指针时。最后,通过实例展示了如何正确地进行拷贝构造函数和赋值函数的实现。

转自:http://blog.youkuaiyun.com/keyouan2008/article/details/5773031

1-什么时候会用到拷贝构造函数? 
2-什么时候有必要手动写拷贝构造函数?

1-什么时候会用到拷贝构造函数?
       任何你想利用一个已有的类实例给另一个类实例赋值时,这种赋值可能是显式的,也可能是隐式的
显式:classa_1=class_2;
隐式:函数的形参有用到类对象却没有用引用或传址技术时
         函数的返回值是一个对象也没有应用传址技术时
2-什么时候有必要用拷贝构造函数?
上述3种情况,如果没有涉及到深拷贝问题,就没有必要自己来编写拷贝构造函数,编译器有默认的可以很完美的完成任务

 

关于深拷贝
    如果一个类中含有指针成员变量,则在利用一个已存在对象构造新的对象时,就会面临两种选择:深拷贝和浅拷贝。
    浅拷贝只是将对象间对应的指针成员变量进行简单的拷贝,即拷贝结束后新旧对象的指针指向相同的资源(指针的值是相同的);这种拷贝会导致对象的成员不可用,如下例:
     class Person
     {
        public :
        //....
        char * home;                       //the person's home 
        void SetHome(char * str)
        {home = str;}
        ~Person()
         {
           //...
           delete [] home;
         }
     }
    //....
    char * place = new char [20];
    strcpy(place,"China");
    Person *A = new Person();
    A->SetHome(place);
    
    Person * B= Person(A);
    delete A;
   //....
    此时对象A 和对象B的成员home值相同,如果A对象 destroy,则对象B的成员home指向的地址变为不可用(对象A撤销时将home指向的资源释放了)。


    深拷贝是相对于浅拷贝而言的,为了避免上述情况的发生,将上例中的代码改造:对象拷贝时将指针指向的内容拷贝,代码如下:
   class Person
     {
        public :
        //....
        char * home;//the person's home 
        void SetHome(char * str)
        {home = str;}
        
        Person & Person(const Person & per)
        {
             //...
             if(* this == per)//copy itself
     return *this;
             home = new char[strlen(per.home) +1];   //alloc new memory,深拷贝的体现
         strcpy(home,per.home);
         return * this;
        } 
        ~Person()
         {
           //...
           delete [] home;
         }
     }
     深拷贝之后,新旧对象的home成员指向的内容的值相同,而其自身的值不同。这样就可避免出现其中之一destroy 之后,另一对象的home成员不可用。【以上内容为转载】

 

     这里补充一句,当类中包含了需要深拷贝的字符指针时,需要编写拷贝构造函数和赋值函数。


     最后,以前面写的string类中的拷贝构造函数来结束。

     string类必须要自己定义拷贝构造函数,而不能由编译器自动生成。因为m_string为指针,拷贝对象时需要为m_string分配内存空间,即深拷贝。

 

String::String(const String &str) { int len = strlen(str.m_string); m_string = new char[len+1];            //深拷贝的体现 strcpy(m_string,str.m_string); }

 

 

<think>嗯,用户想了解如何编写一个栈。首先,我得确定他们使用哪种编程语言,不过问题里没明确说。常见的选择可能是Python、Java或者C++之的。考虑到用户可能刚开始学编程,Python的语法相对简单,可能更适合作为例子。但为了全面,或许应该提及其他语言的实现方,或者至少说明栈的基本原理,这样用户可以根据不同语言自行调整。 接下来,我需要回忆栈的基本特性:后进先出(LIFO),主要操作有push、pop、peek/top、isEmpty、size等。然后,要决定用什么样的数据结构来实现栈。在Python中,通常用列表,因为列表的append和pop方法刚好符合栈的需求。不过,如果用户用其他语言,比如Java,可能需要用数组或链表,但这里先专注于Python的实现。 然后,开始构思的结构。一个栈需要有实例变量来存储元素,比如一个列表。构造函数里初始化这个列表。然后依次实现各个方法。push方法将元素添加到列表末尾,pop则删除并返回最后一个元素,但要注意处理空栈的情况,可能会抛出异常。peek方法查看栈顶元素,同样需要处理空栈。isEmpty检查列表是否为空,size返回长度。 另外,是否需要考虑栈的最大容量?用户可能想要一个固定大小的栈,这时候push的时候需要检查是否已满。但问题里没提到这点,所以可能基础实现不需要,但可以提一下扩展的可能性。 然后,测试这个栈的例子,比如实例化对象,进行push、pop等操作,确保输出正确。用户可能希望看到具体的示例代码,所以在回答中加入这部分会有帮助。 还要注意错误处理,比如当栈为空时调用pop或peek,应该抛出异常,比如IndexError或者自定义异常。在Python中,如果直接调用列表的pop方法在空列表时会抛出IndexError,所以可以不用额外处理,或者捕获异常并给出友好提示。 可能用户对时间复杂度也有疑问,比如各个操作的时间。比如,在Python中使用列表的话,push和pop的平均时间复杂度是O(1),因为是在末尾操作。但如果使用其他方实现,比如链表,时间复杂度也是似的,但具体实现不同。 另外,是否要提到栈的应用场景?比如函数调用栈、括号匹配等问题。可能用户想知道为什么需要学习栈,所以简要提一下应用会有帮助。 总结步骤的话,应该是:定义,初始化数据结构,实现各个方法,测试。确保每个步骤清晰,代码正确,语法无误。同时,提醒用户注意边界情况,比如空栈时的操作。 可能用户有潜在的疑问,比如如何用其他数据结构实现栈,或者不同语言的区别,但当前问题可能只需要一个简单的Python实现。不过,最好在回答中涵盖这些点,或者至少指出可以根据需要扩展。 最后,检查是否符合用户的要求:结构清晰,步骤明确,正确实现栈,并提供示例用法。确保代码符合Python的语法规范,比如名用大写,方法名用小写等。</think>以下是用Python实现栈的最基础方案,按照软件开发规范逐步说明: 1. 定义栈结构 ```python class Stack: def __init__(self): self.items = [] # 使用列表作为底层存储容器 ``` 2. 实现核心方法 ```python class Stack: # ...初始化代码同上... def push(self, item): """ 入栈操作 时间复杂度O(1) """ self.items.append(item) # 列表末尾即栈顶 def pop(self): """ 出栈操作 时间复杂度O(1) """ if self.is_empty(): raise IndexError("pop from empty stack") return self.items.pop() # 移除并返回最后一个元素 def peek(self): """ 查看栈顶元素 时间复杂度O(1) """ if self.is_empty(): raise IndexError("peek from empty stack") return self.items[-1] # 返回最后一个元素 def is_empty(self): """ 判断栈是否为空 """ return len(self.items) == 0 def size(self): """ 获取栈内元素数量 """ return len(self.items) ``` 3. 完整代码示例 ```python class Stack: def __init__(self): self.items = [] def push(self, item): self.items.append(item) def pop(self): if not self.items: raise IndexError("pop from empty stack") return self.items.pop() def peek(self): if not self.items: raise IndexError("peek from empty stack") return self.items[-1] def is_empty(self): return len(self.items) == 0 def size(self): return len(self.items) ``` 4. 使用示例 ```python # 创建栈实例 s = Stack() # 入栈操作 s.push(10) s.push(20) s.push(30) # 查看栈顶 print(s.peek()) # 输出:30 # 出栈操作 print(s.pop()) # 输出:30 print(s.pop()) # 输出:20 # 检查状态 print(s.is_empty()) # 输出:False print(s.size()) # 输出:1 ``` 5. 关键特性说明: - 后进先出(LIFO)特性 - 时间复杂度均为$O(1)$ - 自动扩容的底层列表实现 - 包含完整的异常处理机制 6. 进阶优化方向: - 添加栈容量限制 - 实现迭代器协议 - 增加型注解 - 支持深拷贝操作 - 添加日志记录功能 这个栈实现满足基本需求,可根据具体应用场景进行扩展。建议重点理解栈的核心操作与底层数据结构的配合关系。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值