第一次尝试
说明
tuple是C++11新标准里的类型。它是一个类似pair类型的模板。
pair类型是每个成员变量各自可以是任意类型,但是只能有俩个成员,而tuple与pair不同的是它可以有任意数量的成员。但是每个确定的tuple类型的成员数目是固定的。
#include<iostream>
//定义在此头文件下
#include<tuple>
using namespace std;
int main()
{
tuple<int,int,float> tp(1,2,3.1);
cout<<"The first element is:\t"<<get<0>(tp)<<endl;
cout<<"The second element is:\t"<<get<1>(tp)<<endl;
cout<<"The third element is:\t"<<get<2>(tp)<<endl;
return 0;
}
| 操作 | 说明 |
|---|---|
| make_tuple(v1,v2,v3,v4…vn) | 返回一个给定初始值初始化的tuple,类型从初始值推断 |
| t1 == t2 | 当俩个tuple具有相同数量的成员且成员对应相等时 |
| t1 != t2 | 与上一个相反 |
| get(t) | 返回t的第i个数据成员 |
| tuple_size::value | 给定了tuple中成员的数量 |
还有一种方法也可以获取元组的值,通过std::tie解包tuple
int x,y;
string a;
std::tie(x,a,y) = tp;
通过tie解包后,tp中三个值会自动赋值给三个变量。
解包时,我们如果只想解某个位置的值时,可以用std::ignore占位符来表示不解某个位置的值。比如我们只想解第三个值时:
std::tie(std::ignore,std::ignore,y) = tp; //只解第三个值了
代码
main.cpp:
#include <iostream>
#include "matrix.h"
using namespace std;
int main()
{
cout << "Hello, world" << endl;
std::vector<float> A = {0.0f, -1.0f, 1.0f, 0.0f};
std::vector<float> B = {0.0f, -1.0f, 1.0f, 0.0f};
std::vector<float> D = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
auto [C, NC, MC] = matrixMultiple(A, B, 2, 2, 2, 2);
auto[E, ME, NE] = matrixMultiple(A, D, 2, 2, 3, 2);
printMatrix(C, NC, MC);
if (E.size() == 0) {
cout << "Error: Dimension mismatch matrix" << endl;
}
else{
printMatrix(E, NE, ME);
}
return 0;
}
matrix.h:
#pragma once
#include <vector>
// 第一次尝试:tuple
std::tuple<std::vector<float>, int, int> matrixMultiple(const std::vector<float>& A, const std::vector<float>& B, int NA, int MA, int NB, int MB);
void printMatrix(const std::vector<float>& A, int N, int M);
matrix.cpp:
#include "matrix.h"
#include <iostream>
// C_ij = \sum_k{A_ik * B_kj}
// 设A为m*p的矩阵,B为p*n的矩阵,那么称m*n的矩阵C为矩阵A与B的乘积,记作C=AB,其中矩阵C中的第i行第j列元素可以表示为:
// (A B)_{i j}=\sum_{k=1}^{p} a_{i k} b_{k j}=a_{i 1} b_{1 j}+a_{i 2} b_{2 j}+\cdots+a_{i p} b_{p j}
std::tuple<std::vector<float>, int, int> matrixMultiple(const std::vector<float>& A, const std::vector<float>& B, int MA, int NA, int MB, int NB){
// 定义一个数组表示最终乘完以后得到的矩阵
int NC = NA;
int MC = MB;
std::vector<float> C(NC * MC, 0);
if (NA != MB) return { {}, 0, 0 };
// 根据矩阵的定义三重for循环计算C各个元素值
for (int i = 0; i < MC; i++){
for (int j = 0; j < NC; j++){
float sum = 0.0f;
for(int k = 0; k < NA; k++){
sum += A[i * NA + k] * B[k * NB + j];
}
C[i * NC + j] = sum;
}
}
// 打包成元组返回
return {C, NC, MC};
}
void printMatrix(const std::vector<float>& A, int N, int M){
for(int i = 0; i < N; i++){
for (int j = 0; j < M; j++){
std::cout << A[i * M + j] << " ";
}
puts("");
}
}
改进
说明
引入类
用类来抽象矩阵这个概念,让这个代码变得简洁好懂。
根据经验,类里面的变量只能强制定义为private类型,函数能够定义成public类型。
变量主要有:矩阵的尺寸、矩阵的实际数据
成员函数主要有:
- 获得、设置矩阵的维度信息
- 获得、设置矩阵的元素值
同时这里我们考虑两种版本:一个是考虑输入矩阵元素值越界的问题(安全版本),另一个不考虑(普通版本)
函数末尾加上const,表示是常量成员函数
类的加入,非成员函数matrixMultiple可以进行改进。
引入optional
在编程中,我们可以有某个类型的值,也可以没有任何值。因此,我们需要一种方法来模拟类似指针的语义,在指针中,我们可以使用nullptr来表示没有值。
处理这个问题的方法是定义一个特定类型的对象,并用一个额外的布尔成员/标志来表示值是否存在。std::optional<>以一种类型安全的方式提供了这样的对象。
std::optional对象只是包含对象的内部内存加上一个布尔标志。因此,大小通常比包含的对象大一个字节。对于某些包含的类型,甚至可能根本没有大小开销,前提是附加信息可以放在包含的对象中。没有分配堆内存。对象使用与所包含类型相同的对齐方式。
然而,std::optional对象不仅仅是向值成员添加布尔标志功能的结构。例如,如果没有值,就不会为所包含的类型调用构造函数(因此,可以为对象提供没有值的默认状态)。
例子:
#include <iostream>
#include <optional>
#include <string>
// convert string to int if possible:
std::optional<int> asInt(const std::string& s)
{
try
{
return std::stoi(s);
}
catch (...)
{
return std::nullopt;
}
}
int main()
{
for (auto s : { "42", " 077", "hello", "0x33" })
{
// convert s to int and use the result if possible:
std::optional<int> oi = asInt(s);
if (oi) {
std::cout << "convert '" << s << "' to int: " << *oi << "\n";
}
else {
std::cout << "can't convert '" << s << "' to int\n";
}
}
return 0;
}
例子结果:
convert '42' to int: 42
convert ' 077' to int: 77
can't convert 'hello' to int
convert '0x33' to int: 0
代码
main.cpp:
#include <iostream>
#include "matrix.h"
using namespace std;
int main()
{
Matrix A(2, 2, 1.0f);
printMatrix(A);
puts("");
Matrix B({ 0.0f, -1.0f, 1.0f, 0.0f }, 2, 2);
Matrix C({ 0.0f, -1.0f, 1.0f, 0.0f }, 2, 2);
Matrix D({ 1.0f, 0.0f,0.0f,0.0f,1.0f, 0.0f,0.0f,0.0f,1.0f }, 3, 3);
auto E = matrixMultiple_check(C, D); // E是std::optional<Matrix>类型
auto F = matrixMultiple(B, C); // F是Matrix类型
printMatrix(E);
puts("");
printMatrix(F);
return 0;
}
matrix.h:
#pragma once
#include <iostream>
#include <vector>
#include <optional>
class Matrix {
// 数据访问
public:
// 获得矩阵维度信息
std::tuple<unsigned int, unsigned int> getSize() const;
// 设置矩阵的维度
void setSize(unsigned int M, unsigned int N, float val = 0.0f);
// 获得矩阵的元素值
std::optional<float> Matrix::getElement_check(unsigned int i, unsigned int j) const;
float getElement(unsigned int i, unsigned int j) const;
// 设置矩阵的元素值
void setElement(float val, unsigned int i, unsigned j);
// 构造
public:
Matrix(unsigned int M, unsigned int N, float val = 0.0f);
Matrix(std::vector<float> buff, unsigned int M, unsigned int N);
// 成员数据
private:
// 尺寸
unsigned int m_M, m_N;
// 实际数据
std::vector<float> m_Data;
};
std::optional<Matrix> matrixMultiple_check(const Matrix& A, const Matrix& B);
Matrix matrixMultiple(const Matrix& A, const Matrix& B);
void printMatrix(const Matrix& A);
void printMatrix(std::optional<Matrix>& A);
matrix.cpp:
#include "matrix.h"
// 构造函数
Matrix::Matrix(unsigned int M, unsigned int N, float val){
m_Data.resize(M * N, val);
m_M = M;
m_N = N;
}
Matrix::Matrix(std::vector<float> buff, unsigned int M, unsigned int N) : m_M(M), m_N(N), m_Data(buff) {}
// 成员函数
std::tuple<unsigned int, unsigned int> Matrix::getSize() const{ // 获得矩阵维度信息
return { m_M, m_N };
}
void Matrix::setSize(unsigned int M, unsigned int N, float val) { // 设置矩阵的维度
m_Data.clear();
m_Data.resize(M*N, val);
m_N = N;
m_M = M;
}
std::optional<float> Matrix::getElement_check(unsigned int i, unsigned int j) const { // 获得矩阵的元素值(check版本)
if (i >= 1 && i <= m_M && j >= 1 && j <= m_N) {
return { m_Data[(i - 1)*m_N + j - 1] };
}
return {};
}
float Matrix::getElement(unsigned int i, unsigned int j) const { // 获得矩阵的元素值
return { m_Data[(i - 1)*m_N + j - 1] };
}
void Matrix::setElement(float val, unsigned int i, unsigned j) { // 设置矩阵的元素值
m_Data[(i - 1)*m_N + j - 1] = val;
}
Matrix matrixMultiple(const Matrix& A, const Matrix& B) {
auto[MA, NA] = A.getSize();
//auto[MB, NB] = B.getSize();
int MB, NB;
std::tie(MB, NB) = B.getSize();
Matrix C(MA, NB, 0.0f);
for (int i = 1; i <= MA; i++) {
for (int j = 1; j <= NB; j++) {
float sum = 0.0f;
for (int k = 1; k <= NA; k++) {
sum += A.getElement(i, k) * B.getElement(k, j);
}
C.setElement(sum, i, j);
}
}
return C;
}
std::optional<Matrix> matrixMultiple_check(const Matrix& A, const Matrix& B) {
auto[MA, NA] = A.getSize();
auto[MB, NB] = B.getSize();
/*int MB, NB;
std::tie(MB, NB) = B.getSize();*/
Matrix C(MA, NB, 0.0f);
if (NA != MB) return {};
for (int i = 1; i <= MA; i++) {
for (int j = 1; j <= NB; j++) {
float sum = 0.0f;
for (int k = 1; k <= NA; k++) {
sum += A.getElement(i, k) * B.getElement(k, j);
}
C.setElement(sum, i, j);
}
}
return { C };
}
void printMatrix(const Matrix& A){
auto[M, N] = A.getSize();
for (int i = 1; i <= M; i++) {
for (int j = 1; j <= N; j++) {
std::cout << A.getElement(i, j) << " ";
}
puts("");
}
return;
}
void printMatrix(std::optional<Matrix>& A) {
if (A) {
auto B = *A;
auto[M, N] = B.getSize();
for (int i = 1; i <= M; i++) {
for (int j = 1; j <= N; j++) {
std::cout << B.getElement(i, j) << " ";
//printf("%f ", B.getElement(i, j));
}
puts("");
}
return;
}
else {
std::cout << "Error: Dimension mismatch matrix." << std::endl;
}
}
结果:
1 1
1 1
Error: Dimension mismatch matrix
-1 0
0 -1
最终版
改进版引进了类,但是性能上仍然有不足。
需要做到像MATLAB那样进行矩阵的乘法
由于这里我们定义了私有变量,需要用getElement访问才能得到相应的信息。
需注意:
对于一个常量对象,只能调用常量函数,换言之,只有常成员函数才有资格操作常量或常对象,没有const关键字说明的函数不能用来操作常对象
如果常量函数里面修改了类成员或者调用了非常量函数,编译器会找出这个错误。
关于常量对象和常量成员函数的使用方法如下:
- 非常量对象调用非常量成员函数 (正确)
- 非常量对象调用常量成员函数 (正确)
- 常量对象调用常量成员函数 (正确)
- 常量对象调用非常量成员函数 (错误)
尽管构造函数必须是非常量成员函数,但它仍然可用于初始化常量对象。
常量对象的“常量性”是在构造函数完成了对象的初始化之后生效的,持续到调用了对象的析构函数。
一个成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。
另外:无法重载仅按类型区分的函数。
我们这里改进了一下:
对"()"这个运算符,我们进行了重载,分别是针对常量对象和非常量对象进行重载。
main.cpp:
#include "matrix.h"
using namespace std;
int main()
{
cout << "A: " << endl;
Matrix A(2, 2, 1.0f);
Matrix::print(A);
puts("");
Matrix B({ 0.0f, -1.0f, 1.0f, 0.0f }, 2, 2);
Matrix C({ 0.0f, -1.0f, 1.0f, 0.0f }, 2, 2);
Matrix D({ 1.0f, 0.0f,0.0f,0.0f,1.0f, 0.0f,0.0f,0.0f,1.0f }, 3, 3);
auto E = Matrix::Multiple_check(C, D);
auto F = Matrix::Multiple(B, C);
auto G = C * B;
cout << "E = C * D: " << endl;
Matrix::print(E);
puts("");
cout << "F = B * C: " << endl;
Matrix::print(F);
puts("");
cout << "E = B * C: " << endl;
Matrix::print(G);
puts("");
cout << "D: " << endl;
Matrix::print(D);
puts("");
cout << "D(After Revised): " << endl;
D(1, 2) = 2;
Matrix::print(D);
return 0;
}
matrix.h:
#pragma once
#include <iostream>
#include <vector>
#include <optional>
class Matrix {
//静态方法
public:
static std::optional<Matrix> Multiple_check(const Matrix& A, const Matrix& B);
static Matrix Multiple(const Matrix& A, const Matrix& B);
static void print(const Matrix& A);
static void print(std::optional<Matrix>& A);
// 数据访问
public:
//运算符operator重载
float& operator() (unsigned int i, unsigned int j); // 非常量函数
const float& operator() (unsigned int i, unsigned int j) const; // 常量函数,常量的Matrix对象可以调用
//element简化getElement、setElement,将它定义为常量函数,这样常量的matrix也可以访问
float element(unsigned int i, unsigned int j) const;
// 构造
public:
Matrix(unsigned int M, unsigned int N, float val = 0.0f);
Matrix(std::vector<float> buff, unsigned int M, unsigned int N);
// 成员数据
private:
// 尺寸
unsigned int m_M, m_N;
// 实际数据
std::vector<float> m_Data;
};
// 普通函数的运算符重载
Matrix operator*(const Matrix& A, const Matrix& B);
matrix.cpp:
#include "matrix.h"
// 构造函数
Matrix::Matrix(unsigned int M, unsigned int N, float val){
m_Data.resize(M * N, val);
m_M = M;
m_N = N;
}
Matrix::Matrix(std::vector<float> buff, unsigned int M, unsigned int N) : m_M(M), m_N(N), m_Data(buff) {}
// 成员函数
Matrix Matrix::Multiple(const Matrix& A, const Matrix& B) {
Matrix C(A.m_M, B.m_N, 0.0f);
for (int i = 1; i <= A.m_M; i++) {
for (int j = 1; j <= B.m_N; j++) {
float sum = 0.0f;
for (unsigned int k = 1; k <= A.m_N; k++) {
sum += A(i, k) * B(k, j);
}
C(i, j) = sum;
}
}
return C;
}
std::optional<Matrix> Matrix::Multiple_check(const Matrix& A, const Matrix& B) {
/*unsigned int MB, NB;
std::tie(MB, NB) = B.getSize();*/
Matrix C(A.m_M, B.m_N, 0.0f);
if (A.m_N != B.m_M) return {};
for (unsigned int i = 1; i <= A.m_M; i++) {
for (unsigned int j = 1; j <= B.m_N; j++) {
float sum = 0.0f;
for (unsigned int k = 1; k <= A.m_N; k++) {
sum += A(i, k) * B(k, j);
}
C(i, j) = sum;
}
}
return { C };
}
void Matrix::print(const Matrix& A){
for (unsigned int i = 1; i <= A.m_M; i++) {
for (unsigned int j = 1; j <= A.m_N; j++) {
std::cout << A(i, j) << " ";
}
puts("");
}
return;
}
void Matrix::print(std::optional<Matrix>& A) {
if (A) {
auto B = *A;
for (unsigned int i = 1; i <= B.m_M; i++) {
for (unsigned int j = 1; j <= B.m_N; j++) {
//std::cout << B.element(i, j) << " ";
std::cout << B(i, j) << " ";
//printf("%f ", B.getElement(i, j));
}
puts("");
}
return;
}
else {
std::cout << "Error: Dimension mismatch matrix" << std::endl;
}
}
float& Matrix::operator() (unsigned int i, unsigned int j) {
return m_Data[(i - 1)*m_N + j - 1];
}
float Matrix::element(unsigned int i, unsigned int j) const{
return m_Data[(i - 1)*m_N + j - 1];
}
Matrix operator*(const Matrix& A, const Matrix& B) {
return Matrix::Multiple(A, B);
}
const float& Matrix::operator() (unsigned int i, unsigned int j) const {
return m_Data[(i - 1)*m_N + j - 1];
}
结果:
A:
1 1
1 1
E = C * D:
Error: Dimension mismatch matrix
F = B * C:
-1 0
0 -1
E = B * C:
-1 0
0 -1
D:
1 0 0
0 1 0
0 0 1
D(After Revised):
1 2 0
0 1 0
0 0 1
本文介绍了C++11中的元组(tuple)特性,展示了如何创建和操作元组,包括获取元素、解包以及错误处理。通过实例演示了如何使用元组进行矩阵乘法,然后引入类来抽象矩阵概念,提高了代码可读性。进一步,文章提到了std::optional用于处理可能缺失的值,避免空指针异常。最后,通过重载运算符和成员函数优化了矩阵乘法的性能和使用体验。

1万+

被折叠的 条评论
为什么被折叠?



