1 C++中使用vector来表示二维数组

  • 声明一个二维数组:
vector<vector<int>> dp(row, vector<int>(col));

将变量dp初始化为一个含有row个元素的vector对象,其中每个元素又都是含有col个元素的vector对象。内部的vector对象的基类型为int,外部vector对象的基类型为 vector< int >。

  • 获取数组的row和col
vector<vector<int>>& grid

int row = grid.size();
int col = grid.at(0).size();

2 自己动手写一个Grid类

尽管使用嵌套的vector对象能够代表二维数组,但是这种方法很不便利,因此考虑到自己写一个Grid类。

  • 代码实现

开发环境:VS2017

/*
以Class Template的形式实现Matrix
*/
#pragma once template <typename ValueType>
class Grid
{
public: class GridRow; Grid(); //默认的构造函数 Grid(int row,int col); ~Grid(); int numRows() const; int numCols() const; void resize(int row,int col); bool inBounds(int row, int col) const; ValueType get(int row, int col);
const ValueType& get(int row, int col) const; void set(int row, int col, ValueType value); GridRow operator[](int row);
const GridRow operator[](int row) const; void deepCopy(const Grid& src)
{
int n = src.m_icol * src.m_irow;
this->element = new ValueType[n];
for(int i = 0;i < n;i++)
{
this->element[i] = src.element[i];
}
this->m_icol = src.m_icol;
this->m_irow = src.m_irow;
} Grid & operator=(const Grid& src)
{
if (this != &src)
{
delete[] this->element;
deepCopy(src);
} return *this;
} Grid(const Grid& src)
{
deepCopy(src);
} Grid<ValueType> operator +(const Grid<ValueType> & m1); Grid<ValueType> operator *(const Grid<ValueType>& m1); ValueType& operator()(int row, int col); void print() const; public:
class iterator : public std::iterator<std::input_iterator_tag,ValueType>
{
public:
iterator(const Grid* gp,int index)
{
this->gp = gp;
this->index = index;
} //拷贝构造函数
iterator(const iterator& it)
{
this->gp = it.gp;
this->index = it.index;
} iterator& operator++()
{
index++;
return *this;
} iterator operator++(int)
{
iterator copy(*this);
operator++();
return copy;
} bool operator==(const iterator& rhs)
{
return (rhs.gp == this->gp) && (rhs.index == this->index);
} bool operator!=(const iterator& rhs)
{
return !(*this == rhs);
} ValueType& operator*()
{
return gp->element[index];
} ValueType* operator->()
{
return &gp->element[index];
}
private:
const Grid* gp; //指向cosnt Grid的指针,让编译器知道迭代器的操作不能改变Grid对象本身
int index;
}; iterator begin() const
{
return iterator(this, 0);
} iterator end() const
{
return iterator(this, this->m_icol * this->m_irow);
}
private: /*定义一个嵌套类*/
class GridRow
{
friend class Grid;
public: ValueType& operator[](int col)
{
if (gp->inBounds(row,col))
{
return gp->element[row * gp->m_icol + col];
}
//else 情况下没有返回值!
}
ValueType operator[](int col) const
{
if (gp->inBounds(row, col))
{
return gp->element[row * gp->m_icol + col];
}
}
private:
GridRow(const Grid* girdRef, int index)
{
gp = const_cast<Grid*>(girdRef);
row = index;
}
GridRow(Grid* girdRef, int index)
{
gp = girdRef;
row = index;
}
Grid* gp;
int row; };
friend class GridRow;
private:
int m_irow;
int m_icol;
ValueType* element;
}; template<typename ValueType>
Grid<ValueType>::Grid()
{
this->element = NULL;
this->m_irow = 0;
this->m_icol = 0;
} template<typename ValueType>
Grid<ValueType>::Grid(int row, int col):m_irow(row),m_icol(col)
{
if (row < 0 || col < 0)
{
//error
}
this->element = NULL;
resize(this->m_irow,this->m_icol);
} template<typename ValueType>
Grid<ValueType>::~Grid()
{
if (this->element != NULL)
{
delete []this->element; //这里恐怕会出错
}
} template<typename ValueType>
void Grid<ValueType>::resize(int row, int col)
{
if (this->element != NULL)
{
delete[]this->element;
}
this->element = new ValueType[row * col];
this->m_icol = col;
this->m_irow = row;
for (int i = 0;i < row * col;i++)
{
this->element[i] = ValueType();
}
} template<typename ValueType>
inline bool Grid<ValueType>::inBounds(int row, int col) const
{
/*对row 和 col 的上下边界都有进行检查*/
return (row >= 0 && col >= 0) && (row < this->m_irow && col < this->m_icol);
} template<typename ValueType>
int Grid<ValueType>::numRows() const
{
return this->m_irow;
} template<typename ValueType>
int Grid<ValueType>::numCols() const
{
return this->m_icol;
} template<typename ValueType>
ValueType Grid<ValueType>::get(int row, int col)
{
if (row > this->m_irow || col > this->m_icol || row < 0 || col < 0)
{
//error
} return this->element[row * this->m_irow + col];
} template<typename ValueType>
const ValueType & Grid<ValueType>::get(int row, int col) const
{
if (row > this->m_irow || col > this->m_icol || row < 0 || col < 0)
{
//error
} return this->element[row * this->m_irow + col];
} template<typename ValueType>
void Grid<ValueType>::set(int row, int col, ValueType value)
{
if (this->element == NULL)
{
//error
} this->element[row * this->m_icol + col] = value;
} template<typename ValueType>
typename Grid<ValueType>::GridRow Grid<ValueType>::operator[](int row)
{
std::cout << typeid(this).name() << std::endl;
return GridRow(this,row);
} template<typename ValueType>
const typename Grid<ValueType>::GridRow Grid<ValueType>::operator[](int row) const
{
std::cout << typeid(this).name() << std::endl;
return GridRow(this,row);
} template<typename ValueType>
Grid<ValueType> Grid<ValueType>::operator+(const Grid<ValueType>& m1)
{
//TODO:确定m1和this的大小相同 若不相同 error
Grid<ValueType> result(m1.m_irow,m1.m_icol); int grid_size = m1.m_icol * m1.m_irow; for (int i = 0;i < grid_size;i++)
{
result.element[i] = this->element[i] + m1.element[i];
} return result;
} template<typename ValueType>
Grid<ValueType> Grid<ValueType>::operator*(const Grid<ValueType>& m1)
{
//TODO:两个矩阵相乘
//Grid<ValueType> result(this->m_irow,m1.m_icol);
//for (int i = 0;i < result.m_irow;i++)
//{
// for (int j = 0;j < result.m_icol;j++)
// {
// result.set(i,j,0);
// for (int k = 0; k < this->m_icol;k++)
// {
// //result
// }
// }
//}
} template<typename ValueType>
ValueType& Grid<ValueType>::operator()(int row, int col)
{
return this->element[row * this->m_irow + this->m_icol];
//return this->get(row, col);
} template<typename ValueType>
void Grid<ValueType>::print() const
{
int col = this->m_icol;
int grid_size = this->m_icol * this->m_irow; for (int i = 0; i < grid_size; ++i)
{
if (i % col == 0)
{
std::cout << std::endl;
}
std::cout << this->element[i] << " ";
}
}

测试代码:

#include "pch.h"
#include <iostream>
#include "grid.h"
using namespace std; int main()
{ Grid<double> grid1; //声明一个double类型的数组 Grid<int> grid(2,2); cout << "row = " << grid.numRows() << endl;
cout << "col = " << grid.numCols() << endl; grid.resize(3, 3); cout << "row = " << grid.numRows() << endl;
cout << "col = " << grid.numCols() << endl; grid.set(0, 0, 1);
grid.set(0, 1, 2);
grid.set(0, 2, 3);
grid.set(1, 0, 4);
grid.set(1, 1, 1);
grid.set(1, 2, 2);
grid.set(2, 0, 3);
grid.set(2, 1, 4);
grid.set(2, 2, 4); cout << "单个读取元素:" << endl;
cout << grid.get(0, 0)
<< grid.get(0, 1)
<< grid.get(0, 2) << endl; cout << "[][]的测试" << endl;
cout << grid[0][0]
<< grid[0][1]
<< grid[0][2] << endl; grid[0][0] = 5;
cout << grid[0][0] << endl; grid.print(); if (grid.inBounds(4,4))
{
cout << "\ngrid中(4,4)存在元素" << endl;
}
else
{
cout << "\ngrid中(4,4)不存在元素" << endl;
} cout << "grid中(0,2)元素为 " << grid.get(0, 2) << endl; const Grid<int> grid2(grid); //调用拷贝构造函数
const Grid<int>* a;
a = &(grid2);
cout << "单个读取元素:" << endl;
cout << grid.get(2, 0)
<< grid.get(2, 1)
<< grid.get(2, 2) << endl;
//grid2.print();
cout << "[][]的测试" << endl; cout << grid2[0][0]
<< grid2[0][1]
<< grid2[0][2] << endl; Grid<int>::iterator it = grid.begin();
cout << *(it) << endl;
it++;
cout << *(it) << endl;
Grid<int>::iterator it1 = grid2.begin(); if (it != it1)
{
cout << "it != it1" << endl;
} return 0;
}

测试的方法是“单元测试”,尽量把每一个函数功能都测试到,上述测试代码的运行截图:

  • 上述代码的不足与问题:

1 测试代码并没有把所有的函数功能都测试到。

2 矩阵相乘的函数没有实现完整。

3 Grid类中的get(),set(),operator [ ](int row)等函数需要做输入参数的检查,当输入的row或col超出范围时应有错误提示。

4 Grid类中的迭代器实现的功能不足。

以下为在调试代码中遇到的错误:

  • 错误的复现

在VS2017中

const int num = 10;
int *p = &num; //编译器报错

必须要把上面的代码修改为:

const int num = 10;
const int *p = &num;

在今天的测试代码中,有如下一行代码,声明了一个const Grid类型的变量grid2:

const Grid<int> grid2(grid);  //调用拷贝构造函数

然后测试运算符[][],测试代码如下:

cout << grid2[0][0]
<< grid2[0][1]
<< grid2[0][2] << endl;

此时VS2017编译器报错:

错误 C2440 无法从“initializer list”转换为“Grid::GridRow”

这个错误很奇怪,根据错误提示:初始化列表无法转换为Grid< int >::GridRow。把这段代码放到gcc中编译调试也会报错。

  • 错误的分析

加断点调试,上述测试代码会首先跳到下面的代码里:

template<typename ValueType>
typename Grid<ValueType>::GridRow Grid<ValueType>::operator[](int row)
{
std::cout << typeid(this).name() << std::endl;
return GridRow(this,row);
} template<typename ValueType>
const typename Grid<ValueType>::GridRow Grid<ValueType>::operator[](int row) const
{
std::cout << typeid(this).name() << std::endl;
return GridRow(this,row);
}

接下来追到GridRow()这个构造函数里,函数实现如下:

GridRow(Grid* girdRef, int index)
{
gp = girdRef;
row = index;
}

函数调用的流程大致如上分析。下面看错误的具体分析

声明了const Grid< int > grid2的类型,由于grid2是const object,所以系统调用的应该是下面这个函数:

template<typename ValueType>
const typename Grid<ValueType>::GridRow Grid<ValueType>::operator[](int row) const
{
std::cout << typeid(this).name() << std::endl;
return GridRow(this,row);
}

在这个函数里,this的类型应该是class Grid< int > const *, 调用GridRow()函数,但是这个函数的第一个参数是Grid * 类型的,也就是说把Grid< int > const * 转换为Grid< int > *,这个时候编译器就会报错。

  • 错误的解决

给GridRow类声明两个构造函数,这两个构造函数分别如下:

		GridRow(const Grid* girdRef, int index)
{
gp = const_cast<Grid*>(girdRef);
row = index;
}
GridRow(Grid* girdRef, int index)
{
gp = girdRef;
row = index;
}

这样class Grid < int > const*就会调用第一个构造函数,因此也不会报错。

  • 参考资料:

    1 《C++程序设计 基础,编程抽象与算法策略》

    2 《Essential C++》

C++中vecotr表示二维数组并自己实现一个Grid类的更多相关文章

  1. C语言中如何将二维数组作为函数的参数传递

    今天写程序的时候要用到二维数组作参数传给一个函数,我发现将二维数组作参数进行传递还不是想象得那么简单里,但是最后我也解决了遇到的问题,所以这篇文章主要介绍如何处理二维数组当作参数传递的情况,希望大家不 ...

  2. 以杨辉三角为例,从内存角度简单分析C语言中的动态二维数组

    学C语言,一定绕不过指针这一大难关,而指针最让人头疼的就是各种指向关系,一阶的指针还比较容易掌握,但一旦阶数一高,就很容易理不清楚其中的指向关系,现在我将通过杨辉三角为例,我会用四种方法从内存的角度简 ...

  3. C++中动态申请二维数组并释放方法

    C/C++中动态开辟一维.二维数组是非常常用的,以前没记住,做题时怎么也想不起来,现在好好整理一下. C++中有三种方法来动态申请多维数组 (1)C中的malloc/free (2)C++中的new/ ...

  4. 如何在C++中动态建立二维数组(转)

    http://blog.sina.com.cn/s/blog_7c073a8d0100qp1w.html http://blog.163.com/wujiaxing009@126/blog/stati ...

  5. 消除VS中动态申请二维数组C6011,C6385,C6386的警告

    动态申请二维数组,无非就是通过指针来实现.@wowpH 过程分三步:1.申请内存,2.使用数组,3.释放内存. 代码如下: /************************************* ...

  6. OpenCV中Mat与二维数组之间的转换

    ---恢复内容开始--- 在OpenCV中将Mat(二维)与二维数组相对应,即将Mat中的每个像素值赋给一个二维数组. 全部代码如下: #include <iostream> #inclu ...

  7. Python创建二维数组(关于list的一个小坑)

    0.目录 1.遇到的问题 2.创建二维数组的办法 3.1 直接创建法 3.2 列表生成式法 3.3 使用模块numpy创建 1.遇到的问题 今天写Python代码的时候遇到了一个大坑,差点就耽误我交作 ...

  8. PHP中如何对二维数组按某个键值进行排序

    $arr=[     array(         'name'=>'张三',         'age'=>28     ),     array(         'name'=> ...

  9. 《剑指offer》 二维数组中的查找

    本题目是<剑指offer>中的题目 二维数组中的查找 题目: 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个 ...

随机推荐

  1. leetcode 374. 猜数字大小(python)

    我们正在玩一个猜数字游戏. 游戏规则如下:我从 1 到 n 选择一个数字. 你需要猜我选择了哪个数字.每次你猜错了,我会告诉你这个数字是大了还是小了.你调用一个预先定义好的接口 guess(int n ...

  2. 【漏洞学习】slowHTTPtest 慢速 DOS 攻击方法 修复方案

    日期:2018-05-28 21:41:59 更新:2019-07-05 23:15:21 作者:Bay0net 介绍:学习一下 slowHTTPtest 的攻击及防御. 0x01. 安装 下载链接 ...

  3. .Net 逆向 Reflector之reflexil使用

    网上下载了一款商用的教育培训类软件,是用.Net写的,标榜的是免费的,但是只能试用一个月,商家很精明,用此方法推广招揽客户,但是公司在这一块却没有预算购买,一开始就想着既然是商用软件,安全机制做的肯定 ...

  4. windows 把ps/2 鼠标当成ps/2键盘了

    真坑口阿 https://zhidao.baidu.com/question/425134865713508932.html 电脑的PS/2鼠标接口认成键盘了 电脑主板技嘉,只有一个PS/2接口.开始 ...

  5. 【Linux 应用编程】进程管理 - 进程、线程和程序

    基本概念 程序和进程的区别 程序是平台相关的二进制文件,只占用磁盘空间.编写完程序代码后,编译为可执行的二进制文件即可. 进程是运行中的程序,占用 CPU.内存等系统资源. 通过 Shell 命令,可 ...

  6. HTML——<body> 计算机代码 【头部在“网站开发”中】

    HTML属性 完整的属性列表 在引用属性值的时候,如果某些属性本身就有双引号——name= 'John "ShotGun" Nelson' 

  7. mysql下载与安装过程

    1:下载MySql 官网下载地址:https://dev.mysql.com/downloads/mysql/ 选择对应的下载文件.(我电脑是64位,所以这下载的是64位的下载文件) 2:解压mysq ...

  8. git.ZC一套命令_稀疏签出(sparse-checkout)

    1. git init git remote add origin https://gitee.com/?????/movieHome.git git config core.sparsechecko ...

  9. C#方法名前的方括号

    1.序列化:[Serializable]public void 方法名(){...} 2.WebServices方法:[WebMethod]public void 方法名(){...} 3.Ajax( ...

  10. 2019JAVA第一次课程总结

    课程总结:到现在为止之,学习专业课程已有两周了,从刚开始的啥也不懂,现在慢慢入门了.最开始我们为JAVA开发了运行环境,然后使用类编写了最简单的输出,然后开始学习了数据类型,这可以在编程中帮我们解决一 ...