前言

这个实验主要用来测试大家对现代 C++ 的掌握程度,实验要求如下:

简单翻译一下上述要求,就是我们需要实现定义在 src/include/primer/p0_starter.h 中的三个类 MatrixRowMatrixRowMatrixOperations,其中 MatrixRowMatrix 的父类,RowMatrixOperations 定义了三个用于数组运算的成员函数:AddMultiplyGEMM(就是 \(\boldsymbol{A}*\boldsymbol{B} + \boldsymbol{C}\))。

代码实现

Matrix 类

抽象基类 Matrix 需要我们编写的代码很少,只要完成构造函数和析构函数即可,下面省略了一些不需要我们写的代码:

template <typename T>
class Matrix {
protected:
/**
*
* Construct a new Matrix instance.
* @param rows The number of rows
* @param cols The number of columns
*
*/
Matrix(int rows, int cols) : rows_(rows), cols_(cols), linear_(new T[rows * cols]) {}
int rows_;
int cols_;
T *linear_; public:
/**
* Destroy a matrix instance.
* TODO(P0): Add implementation
*/
virtual ~Matrix() { delete[] linear_; }
};

linear_ 指向一个由二维矩阵展平而得的一维数组,里面共有 rows * cols 个类型为 T 的元素。由于我们在堆上分配数组的空间使用的是 new T[],所以删除的时候也得用 delete[]

RowMatrix 类

这个类用于表示二维矩阵,需要实现父类 Matrix 中的所有纯虚函数,为了方便访问数据元素,RowMatrix 多定义了一个指针数组 data_,里面的每个元素分别指向了二维矩阵每行首元素的地址:

template <typename T>
class RowMatrix : public Matrix<T> {
public:
/**
* Construct a new RowMatrix instance.
* @param rows The number of rows
* @param cols The number of columns
*/
RowMatrix(int rows, int cols) : Matrix<T>(rows, cols) {
data_ = new T *[rows];
for (int i = 0; i < rows; ++i) {
data_[i] = &this->linear_[i * cols];
}
} /**
* @return The number of rows in the matrix
*/
auto GetRowCount() const -> int override { return this->rows_; } /**
* @return The number of columns in the matrix
*/
auto GetColumnCount() const -> int override { return this->cols_; } /**
* Get the (i,j)th matrix element.
*
* Throw OUT_OF_RANGE if either index is out of range.
*
* @param i The row index
* @param j The column index
* @return The (i,j)th matrix element
* @throws OUT_OF_RANGE if either index is out of range
*/
auto GetElement(int i, int j) const -> T override {
if (i < 0 || i >= GetRowCount() || j < 0 || j >= GetColumnCount()) {
throw Exception(ExceptionType::OUT_OF_RANGE, "The index out of range");
} return data_[i][j];
} /**
* Set the (i,j)th matrix element.
*
* Throw OUT_OF_RANGE if either index is out of range.
*
* @param i The row index
* @param j The column index
* @param val The value to insert
* @throws OUT_OF_RANGE if either index is out of range
*/
void SetElement(int i, int j, T val) override {
if (i < 0 || i >= GetRowCount() || j < 0 || j >= GetColumnCount()) {
throw Exception(ExceptionType::OUT_OF_RANGE, "The index out of range");
} data_[i][j] = val;
} /**
* Fill the elements of the matrix from `source`.
*
* Throw OUT_OF_RANGE in the event that `source`
* does not contain the required number of elements.
*
* @param source The source container
* @throws OUT_OF_RANGE if `source` is incorrect size
*/
void FillFrom(const std::vector<T> &source) override {
if (static_cast<int>(source.size()) != GetRowCount() * GetColumnCount()) {
throw Exception(ExceptionType::OUT_OF_RANGE, "The number of elements of `source` is different from matrix");
} for (int i = 0; i < GetRowCount(); ++i) {
for (int j = 0; j < GetColumnCount(); ++j) {
data_[i][j] = source[i * GetColumnCount() + j];
}
}
} /**
* Destroy a RowMatrix instance.
*/
~RowMatrix() override { delete[] data_; } private:
T **data_;
};

需要注意的是,在 RowMatrix 中访问基类部分的成员(非虚函数)时需要加上 this 指针,不然编译时会报错说找不到指定的成员。

RowMatrixOperations 类

实现该类的三个成员函数之前应该检查数据维度是否匹配,不匹配就返回空指针,否则开个循环遍历二维矩阵完成相关操作即可:

template <typename T>
class RowMatrixOperations {
public:
/**
* Compute (`matrixA` + `matrixB`) and return the result.
* Return `nullptr` if dimensions mismatch for input matrices.
* @param matrixA Input matrix
* @param matrixB Input matrix
* @return The result of matrix addition
*/
static auto Add(const RowMatrix<T> *matrixA, const RowMatrix<T> *matrixB) -> std::unique_ptr<RowMatrix<T>> {
if (matrixA->GetRowCount() != matrixB->GetRowCount() || matrixA->GetColumnCount() != matrixB->GetColumnCount()) {
return std::unique_ptr<RowMatrix<T>>(nullptr);
} auto rows = matrixA->GetRowCount();
auto cols = matrixA->GetColumnCount();
auto matrix = std::make_unique<RowMatrix<T>>(rows, cols);
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
matrix->SetElement(i, j, matrixA->GetElement(i, j) + matrixB->GetElement(i, j));
}
} return matrix;
} /**
* Compute the matrix multiplication (`matrixA` * `matrixB` and return the result.
* Return `nullptr` if dimensions mismatch for input matrices.
* @param matrixA Input matrix
* @param matrixB Input matrix
* @return The result of matrix multiplication
*/
static auto Multiply(const RowMatrix<T> *matrixA, const RowMatrix<T> *matrixB) -> std::unique_ptr<RowMatrix<T>> {
if (matrixA->GetColumnCount() != matrixB->GetRowCount()) {
return std::unique_ptr<RowMatrix<T>>(nullptr);
} auto rows = matrixA->GetRowCount();
auto cols = matrixB->GetColumnCount();
auto matrix = std::make_unique<RowMatrix<T>>(rows, cols);
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
T sum = 0;
for (int k = 0; k < matrixA->GetColumnCount(); ++k) {
sum += matrixA->GetElement(i, k) * matrixB->GetElement(k, j);
}
matrix->SetElement(i, j, sum);
}
} return matrix;
} /**
* Simplified General Matrix Multiply operation. Compute (`matrixA` * `matrixB` + `matrixC`).
* Return `nullptr` if dimensions mismatch for input matrices.
* @param matrixA Input matrix
* @param matrixB Input matrix
* @param matrixC Input matrix
* @return The result of general matrix multiply
*/
static auto GEMM(const RowMatrix<T> *matrixA, const RowMatrix<T> *matrixB, const RowMatrix<T> *matrixC)
-> std::unique_ptr<RowMatrix<T>> {
if (matrixA->GetColumnCount() != matrixB->GetRowCount()) {
return std::unique_ptr<RowMatrix<T>>(nullptr);
}
if (matrixA->GetRowCount() != matrixC->GetRowCount() || matrixB->GetColumnCount() != matrixC->GetColumnCount()) {
return std::unique_ptr<RowMatrix<T>>(nullptr);
} return Add(Multiply(matrixA, matrixB).get(), matrixC);
}
};

测试

打开 test/primer/starter_test.cpp,将各个测试用例里面的 DISABLED_ 前缀移除,比如 TEST(StarterTest, DISABLED_SampleTest) 改为 TEST(StarterTest, SampleTest),之后运行下述命令:

mkdir build
cd build
cmake ..
make starter_test
./test/starter_test

测试结果如下图所示:

总结

这次实验感觉比较简单,主要考察虚函数、模板和动态内存(包括智能指针)的知识,就是没搞明白为什么函数都用尾置返回类型,而且 Google 风格也让人很不习惯,缩进居然只有两格,函数居然开头大写。以上~~

CMU15445 之 Project#0 - C++ Primer 详解的更多相关文章

  1. QuartusII13.0使用教程详解(一个完整的工程建立)

    好久都没有发布自己的博客了,因为最近学校有比赛,从参加到现在都是一脸懵逼,幸亏有bingo大神的教程,让我慢慢走上了VIP之旅,bingo大神的无私奉献精神值得我们每一个业界人士学习,向bingo致敬 ...

  2. IIS7.0 Appcmd 命令详解和定时重启应用池及站点的设置

    IIS7.0 Appcmd 命令详解 废话不说!虽然有配置界面管理器!但是做安装包的时候命令创建是必不可少的!最近使用NSIS制作安装包仔细研究了一下Appcmd的命令,可谓是功能齐全. 上网查了些资 ...

  3. loadrunner11.0 安装破解详解使用教程

    loadrunner11.0 安装破解详解使用教程 来源:互联网 作者:佚名 时间:01-21 10:25:34 [大 中 小] 很多朋友下载了loadrunner11但不是很会使用,这里简单介绍下安 ...

  4. Apache2.2+Tomcat7.0整合配置详解

    一.简单介绍 Apache.Tomcat Apache HTTP Server(简称 Apache),是 Apache 软件基金协会的一个开放源码的网页服务器,可以在 Windows.Unix.Lin ...

  5. IIS7.0 Appcmd 命令详解

    原文 IIS7.0 Appcmd 命令详解 一:准备工作 APPcmd.exe 位于 C:\Windows\System32\inetsrv 目录 使用 Cd c:\Windows\System32\ ...

  6. Android EventBus 3.0 实例使用详解

    EventBus的使用和原理在网上有很多的博客了,其中泓洋大哥和启舰写的非常非常棒,我也是跟着他们的博客学会的EventBus,因为是第一次接触并使用EventBus,所以我写的更多是如何使用,源码解 ...

  7. RxJava2.0的使用详解

    RxJava2.0的使用详解 1,初识RxJava RxJava就是一种用Java语言实现的响应式编程,来创建基于事件的异步程序 RxJava是一个基于事件订阅的异步执行的一个类库,目前比较火的一些技 ...

  8. 转 OAuth 2.0授权协议详解

    http://www.jb51.net/article/54948.htm 作者:阮一峰 字体:[增加 减小] 类型:转载 时间:2014-09-10我要评论 这篇文章主要介绍了OAuth 2.0授权 ...

  9. 命令创建.net core3.0 web应用详解(超详细教程)

    原文:命令创建.net core3.0 web应用详解(超详细教程) 你是不是曾经膜拜那些敲几行代码就可以创建项目的大神,学习了命令创建项目你也可以成为大神,其实命令创建项目很简单. 1.cmd命令行 ...

随机推荐

  1. SSL及GMVPN握手协议详解

    之前写过一篇文章搞懂密码学基础及SSL/TLS协议,主要介绍了加密学的基础,并从整体上对SSL协议做了介绍.由于篇幅原因,SSL握手的详细流程没有深入介绍.本文将拆解握手流程,在消息级别对握手进行详细 ...

  2. Java语言学习day31--8月06日

    今日内容介绍1.正则表达式的定义及使用2.Date类的用法3.Calendar类的用法 ###01正则表达式的概念和作用 * A: 正则表达式的概念和作用 * a: 正则表达式的概述 * 正则表达式也 ...

  3. SpringAOP的源码解析

    一.SpringAOP的概念 一.AOP的基本概念 1.连接点(Joinpoint):可以被增强的方法. 2.切点(Pointcut):实际被增强的方法. 3.通知(Advice)(增强): 3.1. ...

  4. Dom基础(一):attribute和properrty的区别

    properrty:修改对象属性不会体现到html结构中,针对DOM节点自带属性(id,className,style) attribute:修改html属性,会改变html结构,大多可以添加自定义属 ...

  5. delaycall.js 修改表单延迟自动提交的 jQuery / Zepto 插件

    delaycall.js delaycall 是一个 jQuery / Zepto 插件,用于在用户完成某项操作后,延迟指定秒数后自动调动指定函数.如用户输入完内容后,延迟1秒,自动提交表单. Git ...

  6. XCTF练习题---MISC---hong

    XCTF练习题---MISC---hong flag:BCTF{cute&fat_cats_does_not_like_drinking} 解题步骤: 1.观察题目,下载附件 2.下载文件后发 ...

  7. for & while &迭代器

    for (int i = 0; i < 10; i++) { System.out.println("hello"); } int a=100; for (;a<110 ...

  8. CoreWCF 1.0 正式发布,支持 .NET Core 和 .NET 5+ 的 WCF

    CoreWCF 1.0 正式发布,支持 .NET Core 和 .NET 5+ 的 WCF https://devblogs.microsoft.com/dotnet/corewcf-v1-relea ...

  9. ngx-lua实现高级限流方式一

    基于POST请求体中的某个参数限流 背景 电商平台有活动,活动涉及优惠券的抢券,优惠券系统对大并发支持略差,为了保护整体系统平稳,因此在入口Nginx层对抢券接口做了一层限流. 完整实现如下: lua ...

  10. 3┃音视频直播系统之浏览器中通过 WebRTC 直播视频实时录制回放下载

    一.录制分类 在音视频会议.在线教育等系统中,录制是一个特别重要的功能 录制一般分为服务端录制和客户端录制 服务端录制:优点是不用担心客户因自身电脑问题造成录制失败(如磁盘空间不足),也不会因录制时抢 ...