不同初始化方式的构造函数
Constructors for different ways of initialisation
我正在 class Matrix 上编写代码。所以我很难理解构造函数的使用方式。其实我对默认构造函数和参数化构造函数特别怀疑。
class 的默认构造函数:Matrix() 将行和列以及矩阵元素初始化为零。
参数化构造函数:Matrix(int rows, int columns) 初始化传递的值,以及默认值为 0 的 2D 矩阵元素。
我不知道这两个构造函数是如何工作的。不要 class 只有一个构造函数,或者它可以有多个构造函数。
我知道如何编写默认构造函数以及如何编写参数化构造函数。当我们在同一个 class 中编写这两个构造函数时,并帮助了解这两个构造函数如何工作。
这行得通吗?
class Matrix{
private:
int rows;
int columns;
int **mat;
public:
Matrix(int row, int column){
this->rows = row;
this->columns = column;
mat = new int *[row];
for(int i=0;i<rows;i++){
mat[i]=new int[column];
}
}
};
你的代码有很多错误。当 rows
和 columns
仅在运行时已知时,您不能将它们用作数组大小。即使您可以使用 row
和 columns
作为数组大小,在为它们赋值之前,您也是将它们用作数组的大小。此外 this->mat[rows][columns]={0};
试图访问一个超出数组范围的元素,它会调用未定义的行为。对动态大小的数组使用 std::vector
。
是的,一个 class 可以有多个构造函数。调用哪个构造函数由 overload resolution 决定。在下面的示例中,要调用的构造函数可以简单地由传递的参数数量来确定。一般来说,重载解析更复杂(并且超出了这个答案的范围)。
#include <vector>
#include <iostream>
struct Matrix {
int rows;
int columns;
std::vector<std::vector<int>> data;
Matrix() : rows(0),columns(0) {}
Matrix(int rows,int columns) : rows(0),columns(0),data(rows,std::vector<int>(columns)) {}
};
int main() {
Matrix m1;
std::cout << m1.rows() << "\n";
std::cout << m1.columns() << "\n";
Matrix m2{5,10};
std::cout << m2.rows() << "\n";
std::cout << m2.columns() << "\n";
}
请注意 std::vector
也有不止一个构造函数:https://en.cppreference.com/w/cpp/container/vector/vector。 Matrix()
使用向量默认构造函数 (1) 创建一个空向量。 data(rows,std::vector<int>(columns))
通过调用采用大小和值 (3) 的向量构造函数,用向量的向量初始化 data
。
术语“参数化构造函数”用词不当。 “参数化构造函数”和默认构造函数之间的区别是错误和误导的。构造函数可以同时被参数化和默认构造函数。默认构造函数是可以不带参数调用的构造函数。这可能是因为它没有参数或因为它有默认参数。比如上面两个可以等价写成一个。此外,您不需要将 rows
和 columns
存储为成员,因为向量可以通过其 size()
方法告诉您它的大小:
#include <vector>
#include <iostream>
struct Matrix {
std::vector<std::vector<int>> data;
Matrix(int rows=0,int columns=0) : data(rows,std::vector<int>(columns)) {}
size_t rows() { return data.size(); }
size_t columns() {
if (data.size()) return data[0].size();
return 0;
}
};
int main() {
Matrix m1;
std::cout << m1.data.size() << "\n";
Matrix m2{5,10};
std::cout << m2.data.size() << "\n";
std::cout << m2.data[0].size() << "\n";
}
这里Matrix(int rows=0,int columns=0)
是一个默认的构造函数,它是参数化的,因为它可以用以下两者之一调用:
Matrix m1;
Matrix m2{5,10};
不过,也可以通过
调用带默认参数的构造函数
Matrix m3{42};
这可能是不可取的。因此,更好的选择可能是(如 Caleth 所述):
struct Matrix {
std::vector<std::vector<int>> data;
Matrix(int rows,int columns) : rows(0),columns(0),data(rows,std::vector<int>(columns)) {}
Matrix() : Matrix(0,0) {}
};
这使用委托构造函数来避免重复某些代码(自 C+11 起可用)。
PS: 向量的向量并不是一个特别好的数据结构。 std::vector
的优势在于其数据的局部性,但在 std::vector<std::vector<int>>
中丢失了。 std::vector<int>
中的 int
存储在连续内存中。但是 std::vector<std::vector<int>>
中的 int
并没有全部存储在连续的内存中。那是因为元素没有存储在向量中。通常最好也对 2D 情况使用平面 std::vector<int>
并通过索引转换模拟第二维。
是的,对于给定的class,我们可以有多个构造函数。下面给出的示例说明了这一点。
其次 请注意,在标准 C++ 中,an 的大小必须是 编译时间常数。所以当你写道:
int mat[rows][columns];//THIS IS NOT STANDARD C++
以上语句不是标准的C++。
更好的方法是使用 2D std::vector
,如下所示。您可以将此示例作为参考。
#include <iostream>
#include <vector>
class Matrix{
private:
std::size_t rows;
std::size_t columns;
//use a std::vector instead of array
std::vector<std::vector<int>> mat;
public:
//default constructor
Matrix(): rows(0), columns(0), mat() //this uses constructor initiailzer list
{
}
//parameterized constuctor
Matrix(std::size_t pRows, std::size_t pColumns): rows(pRows), columns(pColumns), mat(rows, std::vector<int>(columns))//this also uses constructor initiailzer list
{
}
//member function to display columns and rows of the matrix
void display()
{
for(auto &r: mat)
{
for(auto &element: r)
{
std::cout<<element<<" ";
}
std::cout<<std::endl;
}
std::cout<<"--------"<<std::endl;
}
};
int main()
{
Matrix m1; //this uses default constructor
m1.display();
Matrix m2(5,7);//this uses parameterized constructor
m2.display();
return 0;
}
我所做的一些修改包括:
- 添加了默认构造函数,它使用构造函数初始化列表。
- 添加了参数化构造函数,它使用构造函数初始化列表。
- 添加了一个
display()
函数,打印出矩阵中的所有元素(行和列)。
- 创建了一个对象
m1
,它使用默认构造函数初始化其数据成员 rows
、columns
和 mat
。
- 创建了一个对象
m2
,它使用参数化构造函数初始化其数据成员 rows
、columns
和 mat
。
我想向您推荐任何 C++ 书籍。
一个 class 可以有任意数量的构造函数,只要它们的签名(参数的数量、类型和顺序)都不同。
你的class有一个构造函数,它是默认构造函数。它只能创建零维矩阵。
使用 this->
访问数据成员是可选的,我认为实际使用它的开发人员并不多。
我假设,您不想(希望如此)动态更改矩阵的维度,所以我建议让成员 rows
和 columns
const
.
而且,您必须认识到:C++ 不是解释型语言。它在一天编译,运行 在另一天编译。编译器根本不知道 rows
和 columns
将使用哪些值,因此在成员声明中使用这些维度是行不通的。大多数时候,编译器会抱怨数组的 non-const 维度。
您有几种可能性:在 run-time 确定要使用的维度或在 compile-time 确定。每个都有其优点和缺点。我想对于一个绝对的初学者来说,run-time 方法看起来更简单,但 compile-time 方法要快得多。
class matrix_runtime
{
public:
matrix_runtime(int r, int c)
: rows(r), columns(c)
{
mat = std::make_unique<int[]>(rows*columns);
}
private:
int const rows{};
int const columns{};
std::unique_ptr<int[]> mat;
};
template<size_t Rows, size_t Columns>
class matrix_compiletime
{
public:
matrix_compiletime() = default;
public:
std::array<int, Rows * Colums> mat;
};
如果您在 compile-time 知道要使用哪些尺寸,我强烈建议使用 compile-time 版本。具有不同维度的矩阵将是不同的类型(这就是矩阵的意义,不是吗?),因此如果您尝试编译器会给出错误,从而帮助您避免错误。
这里有一些关于我使用的标准库的文档:
- https://en.cppreference.com/w/cpp/memory/unique_ptr
- https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
- https://en.cppreference.com/w/cpp/container/array
- https://en.cppreference.com/w/cpp/language/templates
如果您想了解 C++ 初始化:https://en.cppreference.com/w/cpp/language/initialization
您的代码中存在很多更基本的问题:
int rows;
int columns;
int mat[rows][columns];
是C++中动态数组的非法定义。数组大小需要是 C++ 中的编译时常量,但是 rows
和 columns
是 而不是 (如果你同时制作 const
,因为 class 的不同实例可能使用不同的值。
即使它是合法的,在您的默认构造函数中,您也会在构造函数中越界访问数组:
mat[rows][columns] = ...
数组大小为 0(这在 C++ 中也是非法的),数组中没有可访问的索引 0。
因此,在我们继续之前,我们首先需要解决这些问题。根据您的需要,有多种选择。
一种非常简单的方法是将数据维护在 std::vector
of std::vectors
:
中
class Matrix
{
std::vector<std::vector<int>> mat;
public:
// ...
};
向量隐式存储有关其内部大小的信息,因此前 rows
和 columns
成员(正确的类型应该是 size_t
, not int
) 是多余的,可以而且应该放弃使用向量信息的函数:
size_t rows() { return mat.size(); }
size_t columns() { return mat.size() == 0 ? 0 : mat[0].size(); }
我假设所有行的大小都相同,否则(即锯齿状数组)我们无法将其应用于列。
vector of vector 是实现动态矩阵的一种非常方便的方法,但是伴随着矩阵元素访问的双重间接成本,需要一个额外的数组(对你来说是透明的,但你需要它来存储行)并且必须单独分配列(同样对您透明)。
如果基于一维数组实现矩阵,效率会更高:
size_t m_columns;
std::vector<int> mat;
现在您需要单独存储列,并且您需要显式计算矩阵中的正确偏移量:
size_t rows() { return mat.size() / m_columns; }
size_t columns() { return m_columns; }
int& at(size_t x, size_t y) { return mat[x * m_columns + y]; }
请注意,您仍然可以提供 m[x][y]
语法,替换 at
函数,但这会变得相当复杂,所以我暂时不考虑它。但是,优点是单一间接访问,因此访问速度更快,不需要额外的内存和一次分配所有内存,因此再次更快。
另一种方法是以模板参数的形式提供常量数组大小:
template<size_t Rows, size_t Columns>
class Matrix
{
int mat[Rows][Columns];
};
这带来了对数组元素的最快访问和最高的类型安全性,因为您可以提供用于转置、加法、乘法...的运算符,这样您就无法将错误维度的矩阵传递给。另一方面,你失去了很多灵活性,例如你不能将不同维度的矩阵直接放入同一个容器(向量、列表或其他)中,因为具有不同参数的模板形成不同的类型(虽然有一些方法,但不太方便,基于多态性)并且需要在已经在代码中键入,i。 e.您无法动态计算矩阵大小。
这两种方法都有用例(基于动态分配和静态类型矩阵),您需要根据您的具体要求select。对于第一次尝试,我建议使用双向量方法,因为它很简单,尽管它有其他缺点。一旦它起作用,您可以切换到一维数组方法...
现在谈谈你的实际问题:
您总是可以 重载 构造函数,即提供多个参数数量 and/or 类型不同的构造函数;然后将根据您提供的论点 select 编辑最合适的(尽管在某些特定情况下可能会出现歧义)。
在您的情况下,您需要提供:
Matrix();
Matrix(size_t rows, size_t columns); // not needed in the template variant,
// dimensions are given via
// template parameters
不过,您应该使用构造函数的初始化列表(不要与 std::initialiser 列表混淆),它可能如下所示:
Matrix() : /*rows(0), columns(0),*/ mat() { }
请记住,我删除了行和列的成员,因此它们也不会再出现在构造函数中,我将它们留在评论中以说明如果我不这样做的话它会是什么样子。请注意,mat()
调用向量的默认构造函数,该构造函数创建一个空构造函数。显式调用它是可选的,您可以将其省略,然后它会被隐式调用。
使用 std::vector
你很幸运:它提供了一个构造函数,其中包含要初始化的元素数量以及将用于所有元素的可选默认值;所以我们可以简单地写:
Matrix(size_t rows, size_t columns) : mat(rows, std::vector<int>(columns)) { }
就是这样...
您完整的 class 可能如下所示(结合上面的所有信息):
class Matrix
{
std::vector<std::vector<int>> mat;
public:
Matrix() = default; // short hand syntax...
Matrix(size_t rows, size_t columns) : mat(rows, std::vector<int>(columns)) { }
size_t rows() { return mat.size(); }
size_t columns() { return mat.size() == 0 ? 0 : mat[0].size(); }
// still providing `at` function as returning a reference to
// a row vector would allow to modify that one e. g. in size
// and thus create an invalid matrix!
int& at(size_t x, size_t y) { return mat[x][y]; }
};
你可能会到此为止,但实际上我们还没有到最后。一旦你完成以上工作(请尝试自己实现,不要只是copy/paste,你不会从中学到任何东西),你可以回到这里下一步:
class Matrix
{
std::vector<std::vector<int>> mat;
public:
Matrix() = default; // short hand syntax...
Matrix(size_t rows, size_t columns) : mat(rows, std::vector<int>(columns)) { }
size_t rows() const { return mat.size(); }
size_t columns() const { return mat.size() == 0 ? 0 : mat[0].size(); }
// still providing `at` function as returning a reference to
// a row vector would allow to modify that one e. g. in size
// and thus create an invalid matrix!
int at(size_t x, size_t y) const { return mat[x][y]; }
int& at(size_t x, size_t y) { return mat[x][y]; }
};
您注意到 const
关键字了吗?它们允许访问声明为 const
的实例,即不可修改的实例。您应该声明所有不修改对象的成员函数const
。
对于at
函数,我提供了两个版本——const版本只是returns一个值,这样就不会(非法)修改矩阵是可能的,而 non-const 一个引用使得矩阵元素可以改变。
在这种情况下,过载解析非常简单:const
在 const
矩阵上过载,非 const
在非 const
矩阵上过载:
Matrix m(1, 1);
Matrix const& mr = m; // const reference!
m.at(0, 0); // non-const at
mr.at(0, 0); // const at
m.rows(); // can call const functions on non-const objects, if there's
// no non-const overload – but not the other way round
到目前为止是基础知识。如果您对 Matrix
class 的 m[x][y]
语法感兴趣,请发表评论,我可能会添加一些额外的行;)
我正在 class Matrix 上编写代码。所以我很难理解构造函数的使用方式。其实我对默认构造函数和参数化构造函数特别怀疑。
class 的默认构造函数:Matrix() 将行和列以及矩阵元素初始化为零。
参数化构造函数:Matrix(int rows, int columns) 初始化传递的值,以及默认值为 0 的 2D 矩阵元素。
我不知道这两个构造函数是如何工作的。不要 class 只有一个构造函数,或者它可以有多个构造函数。 我知道如何编写默认构造函数以及如何编写参数化构造函数。当我们在同一个 class 中编写这两个构造函数时,并帮助了解这两个构造函数如何工作。 这行得通吗?
class Matrix{
private:
int rows;
int columns;
int **mat;
public:
Matrix(int row, int column){
this->rows = row;
this->columns = column;
mat = new int *[row];
for(int i=0;i<rows;i++){
mat[i]=new int[column];
}
}
};
你的代码有很多错误。当 rows
和 columns
仅在运行时已知时,您不能将它们用作数组大小。即使您可以使用 row
和 columns
作为数组大小,在为它们赋值之前,您也是将它们用作数组的大小。此外 this->mat[rows][columns]={0};
试图访问一个超出数组范围的元素,它会调用未定义的行为。对动态大小的数组使用 std::vector
。
是的,一个 class 可以有多个构造函数。调用哪个构造函数由 overload resolution 决定。在下面的示例中,要调用的构造函数可以简单地由传递的参数数量来确定。一般来说,重载解析更复杂(并且超出了这个答案的范围)。
#include <vector>
#include <iostream>
struct Matrix {
int rows;
int columns;
std::vector<std::vector<int>> data;
Matrix() : rows(0),columns(0) {}
Matrix(int rows,int columns) : rows(0),columns(0),data(rows,std::vector<int>(columns)) {}
};
int main() {
Matrix m1;
std::cout << m1.rows() << "\n";
std::cout << m1.columns() << "\n";
Matrix m2{5,10};
std::cout << m2.rows() << "\n";
std::cout << m2.columns() << "\n";
}
请注意 std::vector
也有不止一个构造函数:https://en.cppreference.com/w/cpp/container/vector/vector。 Matrix()
使用向量默认构造函数 (1) 创建一个空向量。 data(rows,std::vector<int>(columns))
通过调用采用大小和值 (3) 的向量构造函数,用向量的向量初始化 data
。
术语“参数化构造函数”用词不当。 “参数化构造函数”和默认构造函数之间的区别是错误和误导的。构造函数可以同时被参数化和默认构造函数。默认构造函数是可以不带参数调用的构造函数。这可能是因为它没有参数或因为它有默认参数。比如上面两个可以等价写成一个。此外,您不需要将 rows
和 columns
存储为成员,因为向量可以通过其 size()
方法告诉您它的大小:
#include <vector>
#include <iostream>
struct Matrix {
std::vector<std::vector<int>> data;
Matrix(int rows=0,int columns=0) : data(rows,std::vector<int>(columns)) {}
size_t rows() { return data.size(); }
size_t columns() {
if (data.size()) return data[0].size();
return 0;
}
};
int main() {
Matrix m1;
std::cout << m1.data.size() << "\n";
Matrix m2{5,10};
std::cout << m2.data.size() << "\n";
std::cout << m2.data[0].size() << "\n";
}
这里Matrix(int rows=0,int columns=0)
是一个默认的构造函数,它是参数化的,因为它可以用以下两者之一调用:
Matrix m1;
Matrix m2{5,10};
不过,也可以通过
调用带默认参数的构造函数Matrix m3{42};
这可能是不可取的。因此,更好的选择可能是(如 Caleth 所述):
struct Matrix {
std::vector<std::vector<int>> data;
Matrix(int rows,int columns) : rows(0),columns(0),data(rows,std::vector<int>(columns)) {}
Matrix() : Matrix(0,0) {}
};
这使用委托构造函数来避免重复某些代码(自 C+11 起可用)。
PS: 向量的向量并不是一个特别好的数据结构。 std::vector
的优势在于其数据的局部性,但在 std::vector<std::vector<int>>
中丢失了。 std::vector<int>
中的 int
存储在连续内存中。但是 std::vector<std::vector<int>>
中的 int
并没有全部存储在连续的内存中。那是因为元素没有存储在向量中。通常最好也对 2D 情况使用平面 std::vector<int>
并通过索引转换模拟第二维。
是的,对于给定的class,我们可以有多个构造函数。下面给出的示例说明了这一点。
其次 请注意,在标准 C++ 中,an 的大小必须是 编译时间常数。所以当你写道:
int mat[rows][columns];//THIS IS NOT STANDARD C++
以上语句不是标准的C++。
更好的方法是使用 2D std::vector
,如下所示。您可以将此示例作为参考。
#include <iostream>
#include <vector>
class Matrix{
private:
std::size_t rows;
std::size_t columns;
//use a std::vector instead of array
std::vector<std::vector<int>> mat;
public:
//default constructor
Matrix(): rows(0), columns(0), mat() //this uses constructor initiailzer list
{
}
//parameterized constuctor
Matrix(std::size_t pRows, std::size_t pColumns): rows(pRows), columns(pColumns), mat(rows, std::vector<int>(columns))//this also uses constructor initiailzer list
{
}
//member function to display columns and rows of the matrix
void display()
{
for(auto &r: mat)
{
for(auto &element: r)
{
std::cout<<element<<" ";
}
std::cout<<std::endl;
}
std::cout<<"--------"<<std::endl;
}
};
int main()
{
Matrix m1; //this uses default constructor
m1.display();
Matrix m2(5,7);//this uses parameterized constructor
m2.display();
return 0;
}
我所做的一些修改包括:
- 添加了默认构造函数,它使用构造函数初始化列表。
- 添加了参数化构造函数,它使用构造函数初始化列表。
- 添加了一个
display()
函数,打印出矩阵中的所有元素(行和列)。 - 创建了一个对象
m1
,它使用默认构造函数初始化其数据成员rows
、columns
和mat
。 - 创建了一个对象
m2
,它使用参数化构造函数初始化其数据成员rows
、columns
和mat
。
我想向您推荐任何 C++ 书籍。
一个 class 可以有任意数量的构造函数,只要它们的签名(参数的数量、类型和顺序)都不同。
你的class有一个构造函数,它是默认构造函数。它只能创建零维矩阵。
使用 this->
访问数据成员是可选的,我认为实际使用它的开发人员并不多。
我假设,您不想(希望如此)动态更改矩阵的维度,所以我建议让成员 rows
和 columns
const
.
而且,您必须认识到:C++ 不是解释型语言。它在一天编译,运行 在另一天编译。编译器根本不知道 rows
和 columns
将使用哪些值,因此在成员声明中使用这些维度是行不通的。大多数时候,编译器会抱怨数组的 non-const 维度。
您有几种可能性:在 run-time 确定要使用的维度或在 compile-time 确定。每个都有其优点和缺点。我想对于一个绝对的初学者来说,run-time 方法看起来更简单,但 compile-time 方法要快得多。
class matrix_runtime
{
public:
matrix_runtime(int r, int c)
: rows(r), columns(c)
{
mat = std::make_unique<int[]>(rows*columns);
}
private:
int const rows{};
int const columns{};
std::unique_ptr<int[]> mat;
};
template<size_t Rows, size_t Columns>
class matrix_compiletime
{
public:
matrix_compiletime() = default;
public:
std::array<int, Rows * Colums> mat;
};
如果您在 compile-time 知道要使用哪些尺寸,我强烈建议使用 compile-time 版本。具有不同维度的矩阵将是不同的类型(这就是矩阵的意义,不是吗?),因此如果您尝试编译器会给出错误,从而帮助您避免错误。
这里有一些关于我使用的标准库的文档:
- https://en.cppreference.com/w/cpp/memory/unique_ptr
- https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
- https://en.cppreference.com/w/cpp/container/array
- https://en.cppreference.com/w/cpp/language/templates
如果您想了解 C++ 初始化:https://en.cppreference.com/w/cpp/language/initialization
您的代码中存在很多更基本的问题:
int rows;
int columns;
int mat[rows][columns];
是C++中动态数组的非法定义。数组大小需要是 C++ 中的编译时常量,但是 rows
和 columns
是 而不是 (如果你同时制作 const
,因为 class 的不同实例可能使用不同的值。
即使它是合法的,在您的默认构造函数中,您也会在构造函数中越界访问数组:
mat[rows][columns] = ...
数组大小为 0(这在 C++ 中也是非法的),数组中没有可访问的索引 0。
因此,在我们继续之前,我们首先需要解决这些问题。根据您的需要,有多种选择。
一种非常简单的方法是将数据维护在 std::vector
of std::vectors
:
class Matrix
{
std::vector<std::vector<int>> mat;
public:
// ...
};
向量隐式存储有关其内部大小的信息,因此前 rows
和 columns
成员(正确的类型应该是 size_t
, not int
) 是多余的,可以而且应该放弃使用向量信息的函数:
size_t rows() { return mat.size(); }
size_t columns() { return mat.size() == 0 ? 0 : mat[0].size(); }
我假设所有行的大小都相同,否则(即锯齿状数组)我们无法将其应用于列。
vector of vector 是实现动态矩阵的一种非常方便的方法,但是伴随着矩阵元素访问的双重间接成本,需要一个额外的数组(对你来说是透明的,但你需要它来存储行)并且必须单独分配列(同样对您透明)。
如果基于一维数组实现矩阵,效率会更高:
size_t m_columns;
std::vector<int> mat;
现在您需要单独存储列,并且您需要显式计算矩阵中的正确偏移量:
size_t rows() { return mat.size() / m_columns; }
size_t columns() { return m_columns; }
int& at(size_t x, size_t y) { return mat[x * m_columns + y]; }
请注意,您仍然可以提供 m[x][y]
语法,替换 at
函数,但这会变得相当复杂,所以我暂时不考虑它。但是,优点是单一间接访问,因此访问速度更快,不需要额外的内存和一次分配所有内存,因此再次更快。
另一种方法是以模板参数的形式提供常量数组大小:
template<size_t Rows, size_t Columns>
class Matrix
{
int mat[Rows][Columns];
};
这带来了对数组元素的最快访问和最高的类型安全性,因为您可以提供用于转置、加法、乘法...的运算符,这样您就无法将错误维度的矩阵传递给。另一方面,你失去了很多灵活性,例如你不能将不同维度的矩阵直接放入同一个容器(向量、列表或其他)中,因为具有不同参数的模板形成不同的类型(虽然有一些方法,但不太方便,基于多态性)并且需要在已经在代码中键入,i。 e.您无法动态计算矩阵大小。
这两种方法都有用例(基于动态分配和静态类型矩阵),您需要根据您的具体要求select。对于第一次尝试,我建议使用双向量方法,因为它很简单,尽管它有其他缺点。一旦它起作用,您可以切换到一维数组方法...
现在谈谈你的实际问题:
您总是可以 重载 构造函数,即提供多个参数数量 and/or 类型不同的构造函数;然后将根据您提供的论点 select 编辑最合适的(尽管在某些特定情况下可能会出现歧义)。
在您的情况下,您需要提供:
Matrix();
Matrix(size_t rows, size_t columns); // not needed in the template variant,
// dimensions are given via
// template parameters
不过,您应该使用构造函数的初始化列表(不要与 std::initialiser 列表混淆),它可能如下所示:
Matrix() : /*rows(0), columns(0),*/ mat() { }
请记住,我删除了行和列的成员,因此它们也不会再出现在构造函数中,我将它们留在评论中以说明如果我不这样做的话它会是什么样子。请注意,mat()
调用向量的默认构造函数,该构造函数创建一个空构造函数。显式调用它是可选的,您可以将其省略,然后它会被隐式调用。
使用 std::vector
你很幸运:它提供了一个构造函数,其中包含要初始化的元素数量以及将用于所有元素的可选默认值;所以我们可以简单地写:
Matrix(size_t rows, size_t columns) : mat(rows, std::vector<int>(columns)) { }
就是这样...
您完整的 class 可能如下所示(结合上面的所有信息):
class Matrix
{
std::vector<std::vector<int>> mat;
public:
Matrix() = default; // short hand syntax...
Matrix(size_t rows, size_t columns) : mat(rows, std::vector<int>(columns)) { }
size_t rows() { return mat.size(); }
size_t columns() { return mat.size() == 0 ? 0 : mat[0].size(); }
// still providing `at` function as returning a reference to
// a row vector would allow to modify that one e. g. in size
// and thus create an invalid matrix!
int& at(size_t x, size_t y) { return mat[x][y]; }
};
你可能会到此为止,但实际上我们还没有到最后。一旦你完成以上工作(请尝试自己实现,不要只是copy/paste,你不会从中学到任何东西),你可以回到这里下一步:
class Matrix
{
std::vector<std::vector<int>> mat;
public:
Matrix() = default; // short hand syntax...
Matrix(size_t rows, size_t columns) : mat(rows, std::vector<int>(columns)) { }
size_t rows() const { return mat.size(); }
size_t columns() const { return mat.size() == 0 ? 0 : mat[0].size(); }
// still providing `at` function as returning a reference to
// a row vector would allow to modify that one e. g. in size
// and thus create an invalid matrix!
int at(size_t x, size_t y) const { return mat[x][y]; }
int& at(size_t x, size_t y) { return mat[x][y]; }
};
您注意到 const
关键字了吗?它们允许访问声明为 const
的实例,即不可修改的实例。您应该声明所有不修改对象的成员函数const
。
对于at
函数,我提供了两个版本——const版本只是returns一个值,这样就不会(非法)修改矩阵是可能的,而 non-const 一个引用使得矩阵元素可以改变。
在这种情况下,过载解析非常简单:const
在 const
矩阵上过载,非 const
在非 const
矩阵上过载:
Matrix m(1, 1);
Matrix const& mr = m; // const reference!
m.at(0, 0); // non-const at
mr.at(0, 0); // const at
m.rows(); // can call const functions on non-const objects, if there's
// no non-const overload – but not the other way round
到目前为止是基础知识。如果您对 Matrix
class 的 m[x][y]
语法感兴趣,请发表评论,我可能会添加一些额外的行;)