我有这个矩阵类,它有一个2d双数组。在构造函数中,您可以指定宽度和高度。我想在宽度为1时创建一个1d数组而不是2d。因为我重载了[]运算符并返回指针。如果只有1行/列,我不想总是写[i] [0]。相反,我想写[i]。 有谁知道如何解决这个问题?
编辑: 为了澄清这一点,我需要这个类来进行矩阵计算,而不仅仅是数组。
答案 0 :(得分:2)
您可以将两种替代类型包装为变体类型(标记的联合)。
但是,您无法使用operator[]
来访问这两种变体,因为每种变体的返回类型都不同。一个会返回对子阵列的引用,而另一个会返回一个double。您不能有两个仅因返回类型而不同的重载。
相反,您可以使用重载函数。例如,double at(size_type x)
和double at(size_type x, size_type y)
。
然而,由于你代表一个矩阵,使用一维数组来表示任意等级的矩阵可能更简单,方法是将更高维度连续放置,就像多维数组存储在内存中一样(内存是一维的) , 毕竟)。这允许您在运行时指定每个维度的宽度,并避免变体类型的复杂性。
答案 1 :(得分:0)
如果您没有C ++ 17编译器,一个不错的解决方案是使用std::variant
或boost::variant
。我会创建一个容易使用它的容器。
template<typename T>
struct DynamicDimension {
std::variant<T, std::vector<T>> element;
// accessors, is_vector and is_element function.
// Maybe operator[] and push_back and a get function.
// Add begin and end to make your class useable with range for loops.
};
答案 2 :(得分:0)
这个问题对我很感兴趣,在我原来的答案中,我说过所谓的操作是不可能的,因为在C ++中你不能重载只有它们的返回类型不同的函数。
但是如果可以的话呢?
我变得很好奇,所以我花了很多时间研究和摆弄C ++,看看我是否可以让一个函数返回多种类型......答案就是&#39;是的,& #39;但这是一种不常用的方式,因为它使用void *
,这使生活变得困难。
那就是说,我从经验中学到了一些东西,所以我想我应该分享一下。
因此,有更少的方法可以用更少的代码来解决这个问题。在矩阵类中,有两个指针,一个是T * matrix
,另一个是T ** matrix2d
,并使用void *
作为重载[]
运算符的返回类型。
#ifndef matrix_h
#define matrix_h
template <typename T>
class Matrix {
private:
int width;
int height;
int size;
T * matrix;
T ** matrix2d;
public:
Matrix(const int w, const int h): width(w), height(h){
if(w==1){
matrix = new T[h];
size = h;
} else if (h==1){
matrix = new T[w];
size = w;
} else {
matrix2d = new T*[h];
for(int i=0;i<h;++i){
matrix2d[i] = new T[w];
}
size = w*h;
}
}
~Matrix() {
if(width==1 || height==1){
delete [] matrix;
} else {
for(int i=0;i<height;++i){
T * _r = matrix2d[i];
delete [] _r;
}
delete [] matrix2d;
}
}
void * operator[](const int i){
if(width==1 || height==1){
return & matrix[i];
} else {
return & (*matrix2d[i]);
}
}
const int getSize(){
return size;
}
};
#endif /* matrix_h */
在main
中,执行以下演示:
#include <iostream>
#include "Matrix.h"
int main() {
//Give the type so the correct size_t is allocated in memory.
Matrix <double> matrix1(1, 3);
Matrix <double> matrix2(2,2);
//Now have an array of void pointers.
std::cout << "1 dimensional" << std::endl;
for(int i=0;i<matrix1.getSize();++i){
std::cout << matrix1[i] << std::endl;
}
//Cast the void *, then dereference it to store values.
*((double*)matrix1[0]) = 77;
*((double*)matrix1[1]) = 31;
*((double*)matrix1[2]) = 24.1;
for(int i=0;i<matrix1.getSize();++i){
std::cout << *((double *)matrix1[i]) << std::endl;
}
std::cout << "2 dimensional addresses." << std::endl;
for(int i=0;i<2;++i){
double * _row = (double*)matrix2[i];
for(int j=0;j<2;++j){
std::cout << &_row[j] << " ";
}
std::cout << std::endl;
}
std::cout << "2 dimensional assignment and display." << std::endl;
double num = 13.1;
for(int i=0;i<2;++i){
double * _row = (double*)matrix2[i];
for(int j=0;j<2;++j){
_row[j] = num;
num += 0.13;
std::cout << _row[j] << " ";
}
std::cout << std::endl;
}
return 0;
}
答案 3 :(得分:0)
我建议使用一维数组,user2079303 suggested in their answer。但是,如果这不可取,那么也可以用模板或多态实现你的想法。
如果您不需要动态更改其尺寸,则可以使用尺寸作为模板参数将矩阵设为模板类。反过来,这允许使用SFINAE将维度用于编译时逻辑,这允许您根据矩阵的维度重载operator[]()
。
请注意,在Rows == 1
或Cols == 1
时,实际不创建一维数组,它只是模拟一个;如果您明确需要这种行为,而不是技术上不同但大多数是等效的行为,那么您将需要考虑在一个或两个参数为1
时专门化该类。这可能涉及至少复制一些代码,所以它会有点混乱。
// This example uses std::array for the actual array, since it's compile-time anyways.
// This, in turn, lets it declare a lot of functions constexpr.
// Const-correctness omitted for brevity. Remember to restore it for actual code.
template<size_t Rows, size_t Cols>
class Matrix {
std::array<std::array<double, Cols>, Rows> mat;
public:
// Default constructor. Clang _really_ likes braced initialiser lists.
constexpr Matrix() : mat{{}} {}
// Array constructor.
constexpr Matrix(const decltype(mat)& arr) : mat(arr) {}
// -----
// Subscript operators.
// Generic operator. Matrix<x, y>, where x != 1 && y != 1.
// Does what normal subscript operators do.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<!R && !C, size_t> i) {
return mat[i];
}
// Magic operator. Matrix<1, x>, where x != 1.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<(R && !C), size_t> i) {
return mat[0][i];
}
// Magic operator. Matrix<x, 1>, where x != 1.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<C && !R, size_t> i) {
return mat[i][0];
}
// Scalar matrix operator. Matrix<1, 1>.
// Just returns mat[0][0], for simplicity's sake. Might want to make it do something
// more complex in your actual class.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<R && C, size_t> i) {
return mat[0][0];
}
// -----
// A few interface helpers.
// Simple begin() & end(), for example's sake.
// A better version would begin at mat[0][0] and end at mat[Rows - 1][Cols - 1].
constexpr auto begin() const { return mat.begin(); }
constexpr auto end() const { return mat.end(); }
// Generic helpers.
constexpr size_t size() const { return mat.size() * mat[0].size(); }
constexpr size_t rows() const { return mat.size(); }
constexpr size_t cols() const { return mat[0].size(); }
// 1D Matrix helpers.
constexpr bool is_one_d() const { return (Rows == 1) || (Cols == 1); }
constexpr bool one_row() const { return Rows == 1; }
constexpr size_t dimension() const { return (one_row() ? cols() : rows()); }
// -----
// Output.
// Would need modification if better begin() & end() are implemented.
friend std::ostream& operator<<(std::ostream& str, const Matrix<Rows, Cols>& m) {
for (auto& row : m) {
for (auto& elem : row) {
str << std::setw(6) << elem << ' ';
}
str << '\n';
}
str << std::endl;
return str;
}
};
它可以用作......
// Get rid of any "Waah, you didn't use that!" warnings.
// See https://stackoverflow.com/a/31654792/5386374
#define UNUSED(x) [&x]{}()
// This should really use if constexpr, but online compilers don't really support it yet.
// Instead, have an SFINAE dummy.
template<size_t Rows, size_t Cols>
void fill(Matrix<Rows, Cols>& m, std::enable_if_t<(Rows == 1) || (Cols == 1), int> dummy = 0) {
UNUSED(dummy);
//for (size_t i = 0; i < (m.one_row() ? m.cols() : m.rows()); i++) {
for (size_t i = 0; i < m.dimension(); i++) {
m[i] = (i ? i : 0.5) * (i ? i : 0.5);
}
}
template<size_t Rows, size_t Cols>
void fill(Matrix<Rows, Cols>& m, std::enable_if_t<!((Rows == 1) || (Cols == 1)), int> dummy = 0) {
UNUSED(dummy);
for (size_t i = 0; i < m.rows(); i++) {
for (size_t j = 0; j < m.cols(); j++) {
m[i][j] = (i ? i : 0.5) * (j ? j : 0.5) + (i >= j ? 0.1 : -0.2);
}
}
}
在行动here中查看。
如果你做需要能够动态改变其尺寸,这就变得更加复杂。最简单的解决方案可能是使用多态,并让operator[]
返回代理。
class Matrix {
protected:
// Out proxy class.
// Visible to children, for implementing.
struct SubscriptProxy {
virtual operator double&() = 0;
virtual operator double*() = 0;
virtual double& operator=(double) = 0;
virtual double& operator[](size_t) = 0;
virtual ~SubscriptProxy() = default;
};
public:
virtual SubscriptProxy& operator[](size_t i) = 0;
virtual ~Matrix() = default;
virtual void out(std::ostream& str) const = 0;
friend std::ostream& operator<<(std::ostream& str, const Matrix& m) {
m.out(str);
return str;
}
};
std::ostream& operator<<(std::ostream& str, const Matrix& m);
然后,您可以让每个类生成所需的代理成员public
,而不是private
; private
得到了一个非功能性的虚拟实现。
// Resizing omitted for brevity.
class OneDMatrix : public Matrix {
double arr[5];
// Proxy for single element.
class OneDProxy : public SubscriptProxy {
double& elem;
operator double*() override { return &elem; }
double& operator[](size_t) override { return elem; }
public:
OneDProxy(double& e) : elem(e) {}
operator double&() override { return elem; }
double& operator=(double d) override {
elem = d;
return elem;
}
};
public:
OneDMatrix() : arr{0} {}
// operator[] maintains a static pointer, to keep the return value alive and guarantee
// proper cleanup.
SubscriptProxy& operator[](size_t i) override {
static OneDProxy* ret = nullptr;
if (ret) { delete ret; }
ret = new OneDProxy(arr[i]);
return *ret;
}
void out(std::ostream& str) const override {
for (size_t i = 0; i < 5; i++) {
str << std::setw(4) << arr[i] << ' ';
}
str << std::endl;
}
};
// Resizing omitted for brevity.
class TwoDMatrix : public Matrix {
double arr[3][4];
// Proxy for array.
class TwoDProxy : public SubscriptProxy {
double* elem;
operator double&() override { return elem[0]; }
double& operator=(double) override { return elem[0]; }
public:
TwoDProxy(double* e) : elem(e) {}
operator double*() override { return elem; }
double& operator[](size_t i) override { return elem[i]; }
};
public:
TwoDMatrix() : arr{{0}} {}
// operator[] maintains a static pointer, to keep the return value alive and guarantee
// proper cleanup.
SubscriptProxy& operator[](size_t i) override {
static TwoDProxy* ret = nullptr;
if (ret) { delete ret; }
ret = new TwoDProxy(arr[i]);
return *ret;
}
void out(std::ostream& str) const override {
for (size_t i = 0; i < 3; i++) {
for (size_t j = 0; j < 4; j++) {
str << std::setw(4) << arr[i][j] << ' ';
}
str << '\n';
}
}
};
由于引用可用于多态,Matrix
可用作接口,允许任何不特别需要实际类之一的Matrix&
。
在行动here中查看。
通过这样的设置,通过提供辅助函数,可以轻松地动态创建正确的矩阵。
// Assume OneDMatrix is expanded for dynamic size specification.
// It now has constructor OneDMatrix(size_t sz), and owns a dynamic double[sz].
// Assume TwoDMatrix is expanded for dynamic size specification.
// It now has constructor TwoDMatrix(size_t r, size_t c), and owns a dynamic double[r][c].
Matrix* createMatrix(size_t rows, size_t cols) {
if (rows == 1) { return new OneDMatrix(cols); }
else if (cols == 1) { return new OneDMatrix(rows); }
else { return new TwoDMatrix(rows, cols); }
}
像往常一样,模板版本更冗长,但更安全(由于不必使用指针)并且可能更高效(由于不需要进行尽可能多的动态分配和解除分配;我还没有测试过但是,这个。
答案 4 :(得分:-1)
要制作2D动态数组,您需要使用一些数据结构概念,我正在考虑您不熟悉它们。在此之前,让我向您展示如何制作一维动态阵列。
int main()
{
int size;
std::cin >> size;
int *array = new int[size];
delete [] array;
return 0;
}
请勿忘记删除您使用new分配的每个数组。
让我们回到2D动态数组。这些数组可以概括为散列表,这是一种常见的数据结构类型。现在这个人已经比我更好地解释了2D阵列How to create dynamic 2D array。点击链接。