移动构造函数错误并使用移动构造函数进行委托
move constructor error and delegating using a move constructor
为什么移动赋值运算符的这个实现会在附图中给出错误
Spreadsheet::Spreadsheet(Spreadsheet&& src) noexcept :Spreadsheet(src.width, src.height) {
this->cells = src.cells; // Shallow copy of data
src.cells = nullptr; src.width = 0; src.height = 0; // Reset the source object, because ownership has been moved!
};
Spreadsheet& Spreadsheet::operator=(Spreadsheet&& rhs) noexcept {
if (this == &rhs) return *this;
for (size_t i = 0; i < width; i++) delete[] cells[i];
delete[] cells;
this->width = rhs.width; this->height = rhs.height;
this->cells = rhs.cells;
rhs.cells = nullptr; rhs.width = 0; rhs.height = 0;
return *this;
};
我附上了完整的资源。
电子表格 class 定义
SPreadsheet.h
#pragma once
#include "SpreadsheetCell.h"
class Spreadsheet
{
public:
Spreadsheet(size_t, size_t);
~Spreadsheet();//1
void setCellAt(size_t, size_t, const SpreadsheetCell&);
void verifyCoordinate(size_t, size_t) const;
void swap(Spreadsheet);
SpreadsheetCell& getCellAt(size_t, size_t);
Spreadsheet(const Spreadsheet&); //copy constructor 2
Spreadsheet& operator=(const Spreadsheet& rhs); //assignment operator 3
Spreadsheet(Spreadsheet&& src) noexcept; // Move constructor 4
Spreadsheet& operator=(Spreadsheet&& rhs) noexcept; // Move assign 5
private:
size_t width = 0;
size_t height = 0;
SpreadsheetCell** cells = nullptr;
};
电子表格 class 实施
Spreadsheet.cpp
#include "stdafx.h"
#include "Spreadsheet.h"
#include<utility>
Spreadsheet::Spreadsheet(size_t width, size_t height) :width(width), height(height)
{
cells = new SpreadsheetCell*[width];
for (size_t i = 0; i < height; i++) cells[i] = new SpreadsheetCell[height];
}
void Spreadsheet::verifyCoordinate(size_t x, size_t y) const
{
(x >= width || y >= height) ? throw std::out_of_range("") : void();// void();
}
void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{
verifyCoordinate(x, y);
cells[x][y] = cell;
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y) {
verifyCoordinate(x, y);
return cells[x][y];
}
Spreadsheet::~Spreadsheet()
{
for (size_t i = 0; i < width; i++) {
delete[] cells[i];
};
delete[] cells;
cells = nullptr;
}
Spreadsheet::Spreadsheet(const Spreadsheet& src) :Spreadsheet(src.width, src.height) {
for (size_t i = 0; i < width; i++)
for (size_t j = 0; j < height; j++) cells[i][j] = src.cells[i][j];
}
void Spreadsheet::swap(Spreadsheet copyOfRhsDueToBassByVal) {
std::swap(copyOfRhsDueToBassByVal.width, this->width);
std::swap(copyOfRhsDueToBassByVal.height, this->height);
std::swap(copyOfRhsDueToBassByVal.cells, this->cells);
}
Spreadsheet& Spreadsheet::operator=(const Spreadsheet &rhs) {
if (this == &rhs) return *this;//we cant use return rhs because it is const but the function header returnning a non-const;
swap(rhs); return *this;
}
Spreadsheet::Spreadsheet(Spreadsheet&& src) noexcept :Spreadsheet(src.width, src.height) {
this->cells = src.cells; // Shallow copy of data
src.cells = nullptr; src.width = 0; src.height = 0; // Reset the source object, because ownership has been moved!
};
Spreadsheet& Spreadsheet::operator=(Spreadsheet&& rhs) noexcept {
if (this == &rhs) return *this;
for (size_t i = 0; i < width; i++) delete[] cells[i];
delete[] cells;
this->width = rhs.width; this->height = rhs.height;
this->cells = rhs.cells;
rhs.cells = nullptr; rhs.width = 0; rhs.height = 0;
return *this;
};
SpreadsheetCell class 定义
SPreadsheetCell.h
#pragma once
#include <string>
#include <string_view>
#include<iostream>
using namespace std;
class SpreadsheetCell
{
public:
SpreadsheetCell() = default;
SpreadsheetCell(double initialValue);
SpreadsheetCell(std::string_view initialValue);
void setValue(double);
double getValue() const;
void setString(std::string_view);
string getString() const;
private:
string doubleToString(double) const;
double stringToDouble(std::string_view) const;
double value = 0;
};
SpreadsheetCell class 实现
Spreadsheetcell.cpp
#include "stdafx.h"
#include "SpreadSheetCell.h"
SpreadsheetCell::SpreadsheetCell(double value) :value(value) {};
SpreadsheetCell::SpreadsheetCell(std::string_view strv) { value = stringToDouble(strv); };
void SpreadsheetCell::setValue(double value) { this->value = value; };
double SpreadsheetCell::getValue() const { return value; };
void SpreadsheetCell::setString(std::string_view str) { value = stringToDouble(str); };
string SpreadsheetCell::getString() const { return doubleToString(value); };
string SpreadsheetCell::doubleToString(double inValue) const {
return to_string(inValue);
}
double SpreadsheetCell::stringToDouble(string_view strv) const {
return strtod(strv.data(), nullptr);
}
您的代码中存在多个逻辑错误:
您的分配构造函数没有正确循环一维数组。您正在分配一个包含 width
个元素的数组,然后循环遍历它,就好像它有 height
个元素一样。如果 width < height
,则超出了数组的边界并损坏了内存。如果 width > height
,您不填充整个数组,留下不确定值的指针。
您的移动构造函数正在泄漏内存。它委托给分配构造函数而不是默认构造函数,因此分配了一个新数组,然后它泄漏了该数组。移动构造函数根本不应该分配任何东西。
您的 swap()
交换不正确。 swap()
的目的是相互交换两个对象的内容,但是你的输入参数是按值传递的,所以传递给它的任何对象都会先被复制,然后你才与复制的对象进行交换,不是原来的对象。原始对象不变。因此,您必须改为通过引用传递参数。
此外,移动构造函数和移动赋值运算符的典型且更可取的实现是简单地交换移动对象和移动对象的内容。让移出对象的析构函数释放所有旧资源。不要浪费时间在移动新资源之前释放旧资源。
尝试更像这样的东西:
#pragma once
#include "SpreadsheetCell.h"
class Spreadsheet
{
public:
Spreadsheet() = default;
Spreadsheet(size_t, size_t);
Spreadsheet(const Spreadsheet &);
Spreadsheet(Spreadsheet &&) noexcept;
~Spreadsheet();
Spreadsheet& operator=(const Spreadsheet &);
Spreadsheet& operator=(Spreadsheet &&) noexcept;
SpreadsheetCell& getCellAt(size_t, size_t);
void setCellAt(size_t, size_t, const SpreadsheetCell&);
void swap(Spreadsheet &);
private:
size_t width = 0;
size_t height = 0;
SpreadsheetCell** cells = nullptr;
void verifyCoordinate(size_t, size_t) const;
};
void swap(Spreadsheet &lhs, Spreadsheet &rhs);
#include "stdafx.h"
#include "Spreadsheet.h"
#include <utility>
Spreadsheet::Spreadsheet() noexcept
: cells(nullptr), width(0), height(0)
{
}
Spreadsheet::Spreadsheet(int width, int height)
: cells(nullptr), width(width), height(height)
{
cells = new SpreadsheetCell*[width];
for (size_t i = 0; i < width; ++i)
cells[i] = new SpreadsheetCell[height];
}
Spreadsheet::Spreadsheet(const Spreadsheet &src)
: Spreadsheet(src.width, src.height)
{
for (size_t i = 0; i < width; ++i)
for (size_t j = 0; j < height; ++j)
cells[i][j] = src.cells[i][j];
}
Spreadsheet::Spreadsheet(Spreadsheet &&src) noexcept
: Spreadsheet()
{
src.swap(*this);
}
Spreadsheet::~Spreadsheet()
{
for (size_t i = 0; i < width; ++i)
delete[] cells[i];
delete[] cells;
}
Spreadsheet& Spreadsheet::operator=(const Spreadsheet &rhs)
{
if (this != &rhs)
Spreadsheet(rhs).swap(*this);
return *this;
}
Spreadsheet& Spreadsheet::operator=(Spreadsheet &&rhs) noexcept
{
rhs.swap(*this);
return *this;
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y)
{
verifyCoordinate(x, y);
return cells[x][y];
}
void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{
verifyCoordinate(x, y);
cells[x][y] = cell;
}
void Spreadsheet::swap(Spreadsheet &other)
{
std::swap(cells, other.cells);
std::swap(width, other.width);
std::swap(height, other.height);
}
void Spreadsheet::verifyCoordinate(size_t x, size_t y) const
{
if (x >= width || y >= height)
throw std::out_of_range("");
}
void swap(Spreadsheet &lhs, Spreadsheet &rhs)
{
lhs.swap(rhs);
}
也就是说,如果您使用 std::vector
而不是原始数组,则可以大大简化此代码:
#pragma once
#include "SpreadsheetCell.h"
#include <vector>
class Spreadsheet
{
public:
Spreadsheet() = default;
Spreadsheet(size_t, size_t);
Spreadsheet(const Spreadsheet &) = default;
Spreadsheet(Spreadsheet &&) noexcept = default;
Spreadsheet& operator=(const Spreadsheet &) = default;
Spreadsheet& operator=(Spreadsheet &&) noexcept = default;
SpreadsheetCell& getCellAt(size_t, size_t);
void setCellAt(size_t, size_t, const SpreadsheetCell&);
void swap(Spreadsheet &);
private:
std::vector<std::vector<SpreadsheetCell>> cells;
};
void swap(Spreadsheet &lhs, Spreadsheet &rhs);
#include "stdafx.h"
#include "Spreadsheet.h"
#include <utility>
Spreadsheet::Spreadsheet(int width, int height)
{
cells.resize(width);
for (size_t i = 0; i < width; ++i)
cells[i].resize(height);
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y)
{
return cells.at(x).at(y);
}
void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{
cells.at(x).at(y) = cell;
}
void Spreadsheet::swap(Spreadsheet &other)
{
std::swap(cells, other.cells);
}
void swap(Spreadsheet &lhs, Spreadsheet &rhs)
{
lhs.swap(rhs);
}
为什么移动赋值运算符的这个实现会在附图中给出错误
Spreadsheet::Spreadsheet(Spreadsheet&& src) noexcept :Spreadsheet(src.width, src.height) {
this->cells = src.cells; // Shallow copy of data
src.cells = nullptr; src.width = 0; src.height = 0; // Reset the source object, because ownership has been moved!
};
Spreadsheet& Spreadsheet::operator=(Spreadsheet&& rhs) noexcept {
if (this == &rhs) return *this;
for (size_t i = 0; i < width; i++) delete[] cells[i];
delete[] cells;
this->width = rhs.width; this->height = rhs.height;
this->cells = rhs.cells;
rhs.cells = nullptr; rhs.width = 0; rhs.height = 0;
return *this;
};
我附上了完整的资源。
电子表格 class 定义 SPreadsheet.h
#pragma once
#include "SpreadsheetCell.h"
class Spreadsheet
{
public:
Spreadsheet(size_t, size_t);
~Spreadsheet();//1
void setCellAt(size_t, size_t, const SpreadsheetCell&);
void verifyCoordinate(size_t, size_t) const;
void swap(Spreadsheet);
SpreadsheetCell& getCellAt(size_t, size_t);
Spreadsheet(const Spreadsheet&); //copy constructor 2
Spreadsheet& operator=(const Spreadsheet& rhs); //assignment operator 3
Spreadsheet(Spreadsheet&& src) noexcept; // Move constructor 4
Spreadsheet& operator=(Spreadsheet&& rhs) noexcept; // Move assign 5
private:
size_t width = 0;
size_t height = 0;
SpreadsheetCell** cells = nullptr;
};
电子表格 class 实施 Spreadsheet.cpp
#include "stdafx.h"
#include "Spreadsheet.h"
#include<utility>
Spreadsheet::Spreadsheet(size_t width, size_t height) :width(width), height(height)
{
cells = new SpreadsheetCell*[width];
for (size_t i = 0; i < height; i++) cells[i] = new SpreadsheetCell[height];
}
void Spreadsheet::verifyCoordinate(size_t x, size_t y) const
{
(x >= width || y >= height) ? throw std::out_of_range("") : void();// void();
}
void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{
verifyCoordinate(x, y);
cells[x][y] = cell;
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y) {
verifyCoordinate(x, y);
return cells[x][y];
}
Spreadsheet::~Spreadsheet()
{
for (size_t i = 0; i < width; i++) {
delete[] cells[i];
};
delete[] cells;
cells = nullptr;
}
Spreadsheet::Spreadsheet(const Spreadsheet& src) :Spreadsheet(src.width, src.height) {
for (size_t i = 0; i < width; i++)
for (size_t j = 0; j < height; j++) cells[i][j] = src.cells[i][j];
}
void Spreadsheet::swap(Spreadsheet copyOfRhsDueToBassByVal) {
std::swap(copyOfRhsDueToBassByVal.width, this->width);
std::swap(copyOfRhsDueToBassByVal.height, this->height);
std::swap(copyOfRhsDueToBassByVal.cells, this->cells);
}
Spreadsheet& Spreadsheet::operator=(const Spreadsheet &rhs) {
if (this == &rhs) return *this;//we cant use return rhs because it is const but the function header returnning a non-const;
swap(rhs); return *this;
}
Spreadsheet::Spreadsheet(Spreadsheet&& src) noexcept :Spreadsheet(src.width, src.height) {
this->cells = src.cells; // Shallow copy of data
src.cells = nullptr; src.width = 0; src.height = 0; // Reset the source object, because ownership has been moved!
};
Spreadsheet& Spreadsheet::operator=(Spreadsheet&& rhs) noexcept {
if (this == &rhs) return *this;
for (size_t i = 0; i < width; i++) delete[] cells[i];
delete[] cells;
this->width = rhs.width; this->height = rhs.height;
this->cells = rhs.cells;
rhs.cells = nullptr; rhs.width = 0; rhs.height = 0;
return *this;
};
SpreadsheetCell class 定义 SPreadsheetCell.h
#pragma once
#include <string>
#include <string_view>
#include<iostream>
using namespace std;
class SpreadsheetCell
{
public:
SpreadsheetCell() = default;
SpreadsheetCell(double initialValue);
SpreadsheetCell(std::string_view initialValue);
void setValue(double);
double getValue() const;
void setString(std::string_view);
string getString() const;
private:
string doubleToString(double) const;
double stringToDouble(std::string_view) const;
double value = 0;
};
SpreadsheetCell class 实现
Spreadsheetcell.cpp
#include "stdafx.h"
#include "SpreadSheetCell.h"
SpreadsheetCell::SpreadsheetCell(double value) :value(value) {};
SpreadsheetCell::SpreadsheetCell(std::string_view strv) { value = stringToDouble(strv); };
void SpreadsheetCell::setValue(double value) { this->value = value; };
double SpreadsheetCell::getValue() const { return value; };
void SpreadsheetCell::setString(std::string_view str) { value = stringToDouble(str); };
string SpreadsheetCell::getString() const { return doubleToString(value); };
string SpreadsheetCell::doubleToString(double inValue) const {
return to_string(inValue);
}
double SpreadsheetCell::stringToDouble(string_view strv) const {
return strtod(strv.data(), nullptr);
}
您的代码中存在多个逻辑错误:
您的分配构造函数没有正确循环一维数组。您正在分配一个包含
width
个元素的数组,然后循环遍历它,就好像它有height
个元素一样。如果width < height
,则超出了数组的边界并损坏了内存。如果width > height
,您不填充整个数组,留下不确定值的指针。您的移动构造函数正在泄漏内存。它委托给分配构造函数而不是默认构造函数,因此分配了一个新数组,然后它泄漏了该数组。移动构造函数根本不应该分配任何东西。
您的
swap()
交换不正确。swap()
的目的是相互交换两个对象的内容,但是你的输入参数是按值传递的,所以传递给它的任何对象都会先被复制,然后你才与复制的对象进行交换,不是原来的对象。原始对象不变。因此,您必须改为通过引用传递参数。
此外,移动构造函数和移动赋值运算符的典型且更可取的实现是简单地交换移动对象和移动对象的内容。让移出对象的析构函数释放所有旧资源。不要浪费时间在移动新资源之前释放旧资源。
尝试更像这样的东西:
#pragma once
#include "SpreadsheetCell.h"
class Spreadsheet
{
public:
Spreadsheet() = default;
Spreadsheet(size_t, size_t);
Spreadsheet(const Spreadsheet &);
Spreadsheet(Spreadsheet &&) noexcept;
~Spreadsheet();
Spreadsheet& operator=(const Spreadsheet &);
Spreadsheet& operator=(Spreadsheet &&) noexcept;
SpreadsheetCell& getCellAt(size_t, size_t);
void setCellAt(size_t, size_t, const SpreadsheetCell&);
void swap(Spreadsheet &);
private:
size_t width = 0;
size_t height = 0;
SpreadsheetCell** cells = nullptr;
void verifyCoordinate(size_t, size_t) const;
};
void swap(Spreadsheet &lhs, Spreadsheet &rhs);
#include "stdafx.h"
#include "Spreadsheet.h"
#include <utility>
Spreadsheet::Spreadsheet() noexcept
: cells(nullptr), width(0), height(0)
{
}
Spreadsheet::Spreadsheet(int width, int height)
: cells(nullptr), width(width), height(height)
{
cells = new SpreadsheetCell*[width];
for (size_t i = 0; i < width; ++i)
cells[i] = new SpreadsheetCell[height];
}
Spreadsheet::Spreadsheet(const Spreadsheet &src)
: Spreadsheet(src.width, src.height)
{
for (size_t i = 0; i < width; ++i)
for (size_t j = 0; j < height; ++j)
cells[i][j] = src.cells[i][j];
}
Spreadsheet::Spreadsheet(Spreadsheet &&src) noexcept
: Spreadsheet()
{
src.swap(*this);
}
Spreadsheet::~Spreadsheet()
{
for (size_t i = 0; i < width; ++i)
delete[] cells[i];
delete[] cells;
}
Spreadsheet& Spreadsheet::operator=(const Spreadsheet &rhs)
{
if (this != &rhs)
Spreadsheet(rhs).swap(*this);
return *this;
}
Spreadsheet& Spreadsheet::operator=(Spreadsheet &&rhs) noexcept
{
rhs.swap(*this);
return *this;
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y)
{
verifyCoordinate(x, y);
return cells[x][y];
}
void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{
verifyCoordinate(x, y);
cells[x][y] = cell;
}
void Spreadsheet::swap(Spreadsheet &other)
{
std::swap(cells, other.cells);
std::swap(width, other.width);
std::swap(height, other.height);
}
void Spreadsheet::verifyCoordinate(size_t x, size_t y) const
{
if (x >= width || y >= height)
throw std::out_of_range("");
}
void swap(Spreadsheet &lhs, Spreadsheet &rhs)
{
lhs.swap(rhs);
}
也就是说,如果您使用 std::vector
而不是原始数组,则可以大大简化此代码:
#pragma once
#include "SpreadsheetCell.h"
#include <vector>
class Spreadsheet
{
public:
Spreadsheet() = default;
Spreadsheet(size_t, size_t);
Spreadsheet(const Spreadsheet &) = default;
Spreadsheet(Spreadsheet &&) noexcept = default;
Spreadsheet& operator=(const Spreadsheet &) = default;
Spreadsheet& operator=(Spreadsheet &&) noexcept = default;
SpreadsheetCell& getCellAt(size_t, size_t);
void setCellAt(size_t, size_t, const SpreadsheetCell&);
void swap(Spreadsheet &);
private:
std::vector<std::vector<SpreadsheetCell>> cells;
};
void swap(Spreadsheet &lhs, Spreadsheet &rhs);
#include "stdafx.h"
#include "Spreadsheet.h"
#include <utility>
Spreadsheet::Spreadsheet(int width, int height)
{
cells.resize(width);
for (size_t i = 0; i < width; ++i)
cells[i].resize(height);
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y)
{
return cells.at(x).at(y);
}
void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{
cells.at(x).at(y) = cell;
}
void Spreadsheet::swap(Spreadsheet &other)
{
std::swap(cells, other.cells);
}
void swap(Spreadsheet &lhs, Spreadsheet &rhs)
{
lhs.swap(rhs);
}