c++面向对象:对象的赋值
对象初始化构造函数和复制构造函数在设计一个类时往往要设计构造函数。一般对象的初始化使用构造函数初始化如果没有构造函数则会使用默认构造函数。还可以用复制构造函数来通过一个已有对象初始化一个新的对象。设计一个类来表现对象的初始化复制构造以及赋值。在Stack.hpp头文件中声明类的成员。//Stack.hpp #pragma once class Stack { public: Stack(int max_size); ~Stack(); //复制构造函数 Stack(const Stack s); bool IsEmpty() const; bool IsFull() const; void Push(int data); void Pop(); int Top() const; private: int* buffer_; int* top_; int capacity_; };在Stack.cpp中实现类中的方法。包括有一个表示大小的参数的构造函数析构函数拷贝构造函数栈空栈满的判断函数以及Push、Pop、Top等方法。//Stack.cpp #include Stack.hpp #include iostream Stack::Stack(int max_size) : capacity_(max_size), //buffer_(new int[max_size]), buffer_(static_castint*(std::malloc(sizeof(int) * max_size))), top_(buffer_) {} Stack::~Stack() { std::free(buffer_); } Stack::Stack(const Stack s) : capacity_(s.capacity_), buffer_(static_castint*(std::malloc(sizeof(int) * s.capacity_))), top_(buffer_ - s.buffer_ s.top_) { std::memcpy(buffer_, s.buffer_, sizeof(int) * s.capacity_); } bool Stack::IsEmpty() const { return top_ buffer_; } bool Stack::IsFull() const { return top_ buffer_ capacity_; } void Stack::Push(int data) { if (this-IsFull()) throw Stack is full!; *top_ data; } void Stack::Pop() { if (this-IsEmpty()) throw Stack is Empty!; top_--; } int Stack::Top() const { if (this-IsEmpty()) throw Stack is Empty!; return *(top_ - 1); }在主函数前引用Stack.hpp头文件。创建一个s1对象并push两个值在使用拷贝构造复制s1创建对象s2。确认s1和s2的栈顶相等。在s2中push一个新的值s1和s2的栈顶变得不相等。创建新的对象s3再使用类默认的赋值运算符重载给s3赋值为s1。这样的赋值会把栈中的指针属性的地址直接复制到新的对象。在给 s3 push 了新的值之后s1、s3的 top_ 属性变得不相等但是s1、s3的buffer却指向同一区域在程序结束调用析构时会重复释放同一块内存造成报错。//OverloadAssignment #include iostream #include Stack.hpp int main() { Stack s1(16); s1.Push(1); s1.Push(2); Stack s2 s1; //调用复制构造函数 std::cout s1 top: s1.Top() std::endl; std::cout s2 top: s2.Top() std::endl; s2.Push(3); std::cout s2 pushed 3 std::endl; std::cout s1 top: s1.Top() std::endl; std::cout s2 top: s2.Top() std::endl; Stack s3(16); s3 s1; //调用赋值运算符重载 std::cout s3 top: s3.Top() std::endl; //观察报错信息s3的析构函数被调用了两次说明s3和s1指向了同一块内存区域导致内存被重复释放了。 }虽然能够正常输出所有结果但是在程序结束后析构对象时由于重复释放相同空间引发报错。s1 top: 2 s2 top: 2 s2 pushed 3 s1 top: 2 s2 top: 3 s1 top: 2 s3 top: 2 s3 pushed 3 s1 top: 2 s3 top: 3针对报错copilot的分析简短结论错误 程序在 std::free(buffer_) 处触发断点/崩溃。根本原因是对象复制后发生了浅拷贝——s3 s1; 使用了编译器生成的默认赋值运算符类声明只有拷贝构造函数未定义 operator导致 s1 和 s3 共享同一 buffer_ 指针程序结束时两次释放同一内存double free/堆破坏因此 free 抛出异常/断点。为什么会发生200字 Stack 定义了自定义拷贝构造函数会分配新缓冲区但没有自定义拷贝赋值运算符编译器生成的默认 operator 做逐成员赋值把 buffer_ 指针从 s1 拷贝到 s3浅拷贝。当两个对象析构时会重复释放同一块内存或在赋值后原对象的缓冲区被提前释放或覆盖导致堆损坏free 崩溃。重载赋值运算符如果需要对自定义对象赋值就需要重载赋值运算符。在Stack.hpp中声明重载赋值运算符的函数。并在Stack.cpp中完成。//重载赋值运算符 //函数的返回值是一个引用指向当前对象。在s3 s2 s1;这样的链式赋值中s2 s1的结果是s2的引用然后s3 s2实际上是s3 (s2 s1)因此s3最终也会得到s1的值。 Stack Stack::operator (const Stack s) { //给自己赋值 if (this s) return *this; //释放原有资源 std::free(buffer_); capacity_ s.capacity_; buffer_ static_castint*(std::malloc(sizeof(int) * s.capacity_)); top_ buffer_ - s.buffer_ s.top_; std::memcpy(buffer_, s.buffer_, sizeof(int) * s.capacity_); return *this; }赋值函数的返回值是一个引用指向当前对象。在s3 s2 s1;这样的链式赋值中s2 s1的结果是s2的引用然后s3 s2实际上是s3 (s2 s1)因此s3最终也会得到s1的值。再去运行之前的主函数就不会发生重复释放内存的报错了。这样的赋值函数参数是自己的类型如果不手动实现会隐含一个默认的方法。这样的赋值函数叫做拷贝赋值函数。与拷贝构造函数相似都是传递一个自身的const引用类型。除了拷贝赋值函数也存在一般的赋值函数以一个复数类为例class Complex { public: Complex(double r, double i) : real_(r), imag_(i) {}; Complex operator (double r) { real_ r; imag_ 0.0; return *this; } private: double real_, imag_; };隐式构造除此之外如果类中没有声明赋值函数但是存在有一个参数的构造函数也可以用隐式构造的方法使用等号给对象赋值。class Complex { public: Complex(double r) : real_(r), imag_(0.0) {}; Complex(double r, double i) : real_(r), imag_(i) {}; public: double real_, imag_; }; int main() { Complex c1(1, 2); //使用了隐式构造函数 Complex c2 45.0; //使用了隐式构造函数 c1 4; std::cout c1.real_ c1.imag_ i std::endl; std::cout c2.real_ c2.imag_ i std::endl; return 0; }如果不希望使用隐式构造可以在构造函数前加explicit关键字explicit翻译是明确的、不加掩饰的计算机中是显式的意思加上explicit的构造函数无法被隐式调用。比如在Stack类中构造函数可以被隐式调用但是这种用法意义不明确如果不希望实现这种用法可以在构造函数前加上explicit。class Stack { public: explicit Stack(int max_size); //…… int main (){ Stack st(16); st 20; //会报错 }比如在复数类中同时定义了另一个构造函数此时使用隐式构造会产生歧义导致程序无法运行可以把这个有两个参数且有初值的构造函数指定为explicit。class Complex { public: Complex(double r) : real_(r), imag_(0.0) {}; explicit Complex(double r 0, double i 0) : real_(r), imag_(i) {}; public: double real_, imag_; }; int main() { Complex c1(1, 2); //使用了隐式构造函数 Complex c2 45.0; //使用了隐式构造函数 c1 4; std::cout c1.real_ c1.imag_ i std::endl; std::cout c2.real_ c2.imag_ i std::endl; return 0; }这样可能没什么意义因为给了初值的构造函数可以用单个参数隐式构造也可以用多个参数隐式构造。class Complex { public: Complex(double r 0, double i 0) : real_(r), imag_(i) {}; public: double real_, imag_; }; int main() { Complex c1(1, 2); //使用了隐式构造函数 Complex c2 45.0; //使用了隐式构造函数 c1 4; std::cout c1.real_ c1.imag_ i std::endl; std::cout c2.real_ c2.imag_ i std::endl; //使用多个参数的隐式构造 c1 { 45, 56 }; std::cout c1.real_ c1.imag_ i std::endl; return 0; }
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2593624.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!