如何用cmake构建一个工程?
构建一个工程的时候,需要做的几件事情(如果下面几件事你知道怎么做了,多大的工程就都不是问题了):
1.源代码在什么位置?
2.头文件在哪里?
3.怎么生成静态或者动态库?
4.程序链接的静态库在哪里?
5.如果工程的代码存放在很多地方,那又该怎么找到它们呢?
https://nicehuster.github.io/2018/04/20/Cmake/
矩阵以及矩阵相乘算法初始版本的代码实现(纯函数版本)
写代码的风格:
要把重复的代码抽象出其内部蕴含的某种逻辑形成积木(模块)比如下面代码中的 printMatrix()函数 就是从main函数的逻辑中里抽象出来的。写代码本身就是一个搭积木的过程,大的模块通过小的模块构成(积木)。 类名第一个字大写其他小写,函数写成第一个字母小写的驼峰状。
c++编译出错无需调试,编译出错其实就是语法的错误,编译器会有详细的错误信息给你,根据错误信息修改代码然后重新编译即可。或者,拿到错误信息可以利用搜索引擎:baidu、google,或者stackoverflow搜索类似问题,这里和python其实类似,python若有语法错误也不能调试,语法错误指程序中含有不符合语法规定的语句。调试是在运行时错误时候用来解决问题的。
main.cpp
#include <stdio.h>
#include <iostream>
#include "matrix.h"
#include <vector>
//尽量减少用指针,因为指针不好操控,所以定义数组就用vector,少用char a[]或char* a
#include <tuple>
//main.cpp里面只能有一个函数叫做 main
//写代码的风格:把重复的代码抽象出其内部蕴含的某种逻辑形成积木(模块),写代码本身就是一个搭积木的过程,大的模块通过小的模块构成(积木)
int main(int argc, char* argv[])
{
std::vector<float> A = { 0.0f,-1.0f,2.0f,0.0f };
std::vector<float> B = { 0.0f,-1.0f,2.0f,0.0f };
//auto[C, NC, MC] = matrixMutiple(A, B, 2, 2, 2, 2);这是c++ 17的新特性,用了符号重载
//C = std::get<0>(result);
//NC = std::get<1>(result);
//MC = std::get<2>(result);
std::tuple<std::vector<float>,int,int> result = matrixMutiple(A, B, 2, 2, 2, 2);
std::vector<float> C;
int NC;
int MC;
std::tie(C, NC, MC) = result;
if (C.size() == 0)
{
printf("ERROR");
}
printMatrix(C, NC, MC);
return 0;
}
matrix.h:
#pragma once
#include <tuple>
#include <vector>//尽量减少用指针,因为指针不好操控,所以定义数组就用vector
//矩阵的乘法,返回值的类型是tuple的模板类,第一个元素是vector,第二个是行数,第三个是列数
std::tuple<std::vector<float>, int, int> matrixMutiple(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 <stdio.h>
#include "matrix.h"
//参数用引用是减少复制的操作,提升程序效率
std::tuple<std::vector<float>, int, int> matrixMutiple(const std::vector<float>& A, const std::vector<float>& B, int NA, int MA, int NB, int MB)
{
//定义一个数组C表示最后乘完以后的矩阵 [NA.MA]*[NB*MB】,所以MA=NB
int NC = NA;
int MC = MB;
std::vector<float> C(NC * MC, 0);//定义一个大小为NC * MC的数组初始化为0,用数组来表示矩阵NC表示行数,MC表示列数
//判断相乘的两个矩阵维度是否匹配
if (MA != NB)
{
NC = 0;
MC = 0;
C = {}; //空的vector
//应该在这就返回了,外面调用的人怎么知道它接受的数据是有问题的
std::tuple<std::vector<float>, int, int> result = std::make_tuple(C, NC, MC);
return result;
}
for (int i = 0; i < NC; i++)
{
for (int j = 0; j < MC; j++)
{
float sum = 0.0f;//整型和浮点型的存储结构不同,所以初始化不用float sum = 0
for (int k = 0; k < MA; k++)
{
sum += A[i * MA + k] * B[k * MB + j];
}
C[i* MC + j] = sum;//MC表示行数
}
}
//打包成元组返回
std::tuple<std::vector<float>, int, int> result = std::make_tuple(C, NC, MC);
return result;
//打包成元组返回
//return {C, NC, MC}; //这是c++ 17的新特性,用了符号重载
}
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++)
{
printf("%f ", A[i * M + j]);
}
printf("\n");
}
}
改进版1: 引入类
初始版本存在的问题就是:函数的参数太多了导致一行写下来太长,但这些函数的参数有几个是命运共同体即可以整合到一起,也就是把命运共同体弄成一个类,所以解决方法是用类来抽象矩阵这个概念,让这个代码变得简洁好懂。
根据经验,类里面的变量只能强制定义为private类型,函数能够定义成public类型。
main.cpp
#include <stdio.h>
#include <iostream>
#include "matrix.h"
#include <vector>
#include <tuple>
//main.cpp里面只能有一个函数叫做 main
//写代码的风格:把重复的代码抽象出其内部蕴含的某种逻辑形成积木(模块),写代码本身就是一个搭积木的过程,大的模块通过小的模块构成(积木)
int main(int argc, char* argv[])
{
//std::vector<float> A = { 0.0f,-1.0f,2.0f,0.0f };
//std::vector<float> B = { 0.0f,-1.0f,2.0f,0.0f };
auto[C, NC, MC] = matrixMutiple(A, B, 2, 2, 2, 2);这是c++ 17的新特性,用了符号重载
//
C = std::get<0>(result);
NC = std::get<1>(result);
MC = std::get<2>(result);
//std::tuple<std::vector<float>,int,int> result = matrixMutiple(A, B, 2, 2, 2, 2);
//std::vector<float> C;
//int NC;
//int MC;
//std::tie(C, NC, MC) = result;
//if (C.size() == 0)
//{
// printf("ERROR");
//}
//printMatrix(C, NC, MC);
//Matrix A(3, 4, 1.0f);
//printMatrix(A);
//Matrix B({ 1.0f,2.0f,4.0f,1.2f }, 2, 2);
//printMatrix(B);
Matrix A({ 0.0f,-1.0f,1.0f,0.0f }, 2, 2);
Matrix B({ 0.0f,-1.0f,1.0f,0.0f }, 2, 2);
auto C = matrixMutiple(A, B);
printMatrix(C);
return 0;
}
matrix.h:
#pragma once
#include <tuple>
#include <vector>//尽量减少用指针,因为指针不好操控,所以定义数组就用vector
#include <optional>
//用类来抽象矩阵这个概念,让这个代码变得简洁好懂
class Matrix
{
//数据访问功能
public:
//设置矩阵的维度,比如B矩阵用一半,想要改变B的size
void setsize(unsigned int N, unsigned int M,float val = 0.0f);
//获得矩阵维度信息
std::tuple<unsigned int, unsigned int> getsize() const;
//因为元素的值为private变量,所以要通过public函数来获得矩阵的元素的值 索引号从1开始
float getElement(unsigned int i, unsigned int j) const;
//设置矩阵的元素值 索引号从1开始
void setElement(float val, unsigned int i, unsigned int j);
//构造
public:
Matrix(unsigned int N, unsigned int M, float val = 0.0f);
Matrix(std::vector<float> buff, unsigned int N, unsigned int M) ;
//成员数据
private:
//实际数据
std::vector<float> m_Data;
//尺寸
unsigned int m_N, m_M;// 矩阵维度不可能小于0,所以用unsigned int类型
};
//矩阵乘法1,返回值的类型是tuple的模板类,第一个元素是vector,第二个是行数,第三个是列数
std::tuple<std::vector<float>, int, int> matrixMutiple(const std::vector<float>& A, const std::vector<float>& B, int NA, int MA, int NB, int MB);
void printMatrix(const Matrix& m);
//矩阵乘法2
Matrix matrixMutiple(const Matrix& A, const Matrix& B);
matrix.cpp:
#include <stdio.h>
#include "matrix.h"
//参数缺省值只能出现在函数的声明中,而不能出现在定义体中
Matrix::Matrix(unsigned int N, unsigned int M, float val)
{
m_Data.resize(N * M, val);
m_N = N;
m_M = M;
}
Matrix::Matrix(std::vector<float> buff, unsigned int N, unsigned int M)
{
//存在隐患就是buff的长度不等于给的N*M大小,构造函数没有返回值 你不好表示他算错了
//所以有两种方法解决,一是异常抛出,外面如果把异常解决了就没问题了,还有一种是assert直接让程序崩溃
m_Data = buff;
m_N = N;
m_M = M;
}
//设置矩阵的维度,比如B矩阵用一半,想要改变B的size
void Matrix::setsize(unsigned int N, unsigned int M, float val)
{
//clear 是为了解决vector resize不会把原有元素的值重置的问题
m_Data.clear();
m_Data.resize(N * M, val);
m_N = N;
m_M = M;
}
//获得矩阵维度信息
std::tuple<unsigned int, unsigned int> Matrix::getsize() const
{
return { m_N, m_M };
}
//因为元素的值为private变量,所以要通过public函数来获得矩阵的元素的值 索引号从1开始
float Matrix::getElement(unsigned int i, unsigned int j) const
{
//越界判定,m_N为行,m_M为列
if (i >= 1 && i <= m_N && j >= 1 && j <= m_M)
{
return m_Data[(i - 1) * m_M + j - 1];
}
}
//设置矩阵的元素值 索引号从1开始
void Matrix::setElement(float val, unsigned int i, unsigned int j)
{
if (i >= 1 && i <= m_N && j >= 1 && j <= m_M)
{
m_Data[(i - 1) * m_M + j - 1] = val;
}
}
std::tuple<std::vector<float>, int, int> matrixMutiple(const std::vector<float>& A, const std::vector<float>& B, int NA, int MA, int NB, int MB)
{
//定义一个数组C表示最后乘完以后的矩阵 [NA.MA]*[NB*MB】,所以MA=NB
int NC = NA;
int MC = MB;
std::vector<float> C(NC * MC, 0);//定义一个大小为NC * MC的数组初始化为0,用数组来表示矩阵NC表示行数,MC表示列数
//判断相乘的两个矩阵维度是否匹配
if (MA != NB)
{
NC = 0;
MC = 0;
C = {}; //空的vector
//应该在这就返回了,外面调用的人怎么知道它接受的数据是有问题的
std::tuple<std::vector<float>, int, int> result = std::make_tuple(C, NC, MC);
return result;
}
for (int i = 0; i < NC; i++)
{
for (int j = 0; j < MC; j++)
{
float sum = 0.0f;//整型和浮点型的存储结构不同,所以初始化不用float sum = 0
for (int k = 0; k < MA; k++)
{
sum += A[i * MA + k] * B[k * MB + j];
}
C[i* MC + j] = sum;//MC表示行数
}
}
//打包成元组返回
std::tuple<std::vector<float>, int, int> result = std::make_tuple(C, NC, MC);
return result;
//打包成元组返回
//return {C, NC, MC}; //这是c++ 17的新特性,用了符号重载
}
void printMatrix(const Matrix& m)
{
auto result = m.getsize();
int M;
int N;
std::tie(N, M) = result;//N是行数
for (unsigned int i = 1; i <= N; i++)
{
for (unsigned int j = 1; j <= M; j++)
{
printf("%f ", m.getElement(i,j));
}
printf("\n");
}
}
Matrix matrixMutiple(const Matrix& A, const Matrix& B)
{
//先获得矩阵A和B的尺寸
auto resultA = A.getsize();
auto resultB = B.getsize();
int MA,MB;
int NA,NB;
std::tie(NA, MA) = resultA;//N是行数
std::tie(NB, MB) = resultB;//N是行数
Matrix C(NA, MB, 0.0f);
for (unsigned int i = 1; i <= NA; i++)
{
for (unsigned int j = 1; j <= MB; j++)
{
float sum = 0.0f;//整型和浮点型的存储结构不同,所以初始化不用float sum = 0
for (unsigned int k = 1; k <= MA; k++)
{
sum += A.getElement(i, k) * B.getElement(k, j);
}
C.setElement(sum,i,j);//MC表示行数
}
}
return C;
}
改进版2
改进版1引进了类,但是性能上仍然有不足。
1.在main函数里,做矩阵乘法计算时调用matrixMutiple这个函数,由于这里我们在矩阵类里定义了私有变量,在matrixMutiple这个函数里需要用getElement访问才能得到相应的信息,但每次访问私有变量都要调用getElement函数会比较慢。所以说为了解决这个问题就把matrixMutiple这个函数定义为类的成员函数,并且定义为static静态函数,这样的话调用就是比较科学。
2. 为了做到像MATLAB那样进行矩阵的乘法直接A=BC,所以优化方法进行了运算符的重载。把重载为普通函数,函数名就是operator,调用函数 operator* (B,C)就可以简写为B*C。
3.在Matrix的成员函数里重载了()符号,函数名就是operator(),如A是Matrix类的对象,重载后就可以这样用,A.operator()(2,1) = 77等效于A(2, 1) = 77。
main.cpp
#include <stdio.h>
#include <iostream>
#include "matrix.h"
#include <vector>
#include <tuple>
//main.cpp里面只能有一个函数叫做 main
//写代码的风格:把重复的代码抽象出其内部蕴含的某种逻辑形成积木(模块),写代码本身就是一个搭积木的过程,大的模块通过小的模块构成(积木)
int main(int argc, char* argv[])
{
Matrix A({ 0.0f,-1.0f,1.0f,0.0f }, 2, 2);
Matrix B({ 0.0f,-1.0f,1.0f,0.0f }, 2, 2);
//printf("%f\n",A.element(1,2));
A(2, 1) = 77;
Matrix::print(A);
printf("hello a world\n");
auto C =operator*(A, B);
auto D = A*B;
Matrix::print(C);
Matrix::print(D);
return 0;
}
matrix.h:
#pragma once
#include <tuple>
#include <vector>//尽量减少用指针,因为指针不好操控,所以定义数组就用vector
#include <optional>
//用类来抽象矩阵这个概念,让这个代码变得简洁好懂
class Matrix
{
//静态方法(公有的方法,和独立的对象无关)
public:
//打印不是矩阵独一无二的属性,所以不定义为对象方法
static void print(const Matrix& m);
static Matrix mulptiple1(const Matrix& A, const Matrix& B);
//数据访问功能
public:
//设置矩阵的维度,比如B矩阵用一半,想要改变B的size
void setsize(unsigned int N, unsigned int M,float val = 0.0f);
//获得矩阵维度信息
std::tuple<unsigned int, unsigned int> getsize() const;
//因为元素的值为private变量,所以要通过public函数来获得矩阵的元素的值 索引号从1开始
float getElement(unsigned int i, unsigned int j) const;
//设置矩阵的元素值 索引号从1开始
void setElement(float val, unsigned int i, unsigned int j);
//返回值是float类型的引用,意思是如果对返回值进行更改,引用的那个变量值也会直接跟着变
float& element(unsigned int i, unsigned int j);
//返回值是float类型的引用,即返回的值是一个左值
float& operator()(unsigned int i, unsigned int j);
//构造
public:
Matrix(unsigned int N, unsigned int M, float val = 0.0f);
Matrix(std::vector<float> buff, unsigned int N, unsigned int M) ;
//成员数据
private:
//实际数据
std::vector<float> m_Data;
//尺寸
unsigned int m_N, m_M;// 矩阵维度不可能小于0,所以用unsigned int类型
};
Matrix operator*(const Matrix& A, const Matrix& B);
matrix.cpp
#include <stdio.h>
#include "matrix.h"
//参数缺省值只能出现在函数的声明中,而不能出现在定义体中
Matrix::Matrix(unsigned int N, unsigned int M, float val)
{
m_Data.resize(N * M, val);
m_N = N;
m_M = M;
}
Matrix::Matrix(std::vector<float> buff, unsigned int N, unsigned int M)
{
//存在隐患就是buff的长度不等于给的N*M大小,构造函数没有返回值 你不好表示他算错了
//所以有两种方法解决,一是异常抛出,外面如果把异常解决了就没问题了,还有一种是assert直接让程序崩溃
m_Data = buff;
m_N = N;
m_M = M;
}
//设置矩阵的维度,比如B矩阵用一半,想要改变B的size
void Matrix::setsize(unsigned int N, unsigned int M, float val)
{
//clear 是为了解决vector resize不会把原有元素的值重置的问题
m_Data.clear();
m_Data.resize(N * M, val);
m_N = N;
m_M = M;
}
//获得矩阵维度信息
std::tuple<unsigned int, unsigned int> Matrix::getsize() const
{
return { m_N, m_M };
}
//因为元素的值为private变量,所以要通过public函数来获得矩阵的元素的值 索引号从1开始
float Matrix::getElement(unsigned int i, unsigned int j) const
{
//越界判定,m_N为行,m_M为列
if (i >= 1 && i <= m_N && j >= 1 && j <= m_M)
{
return m_Data[(i - 1) * m_M + j - 1];
}
}
//设置矩阵的元素值 索引号从1开始
void Matrix::setElement(float val, unsigned int i, unsigned int j)
{
if (i >= 1 && i <= m_N && j >= 1 && j <= m_M)
{
m_Data[(i - 1) * m_M + j - 1] = val;
}
}
void Matrix::print(const Matrix& m)
{
for (unsigned int i = 0; i < m.m_N; i++)
{
for (unsigned int j = 0; j < m.m_M; j++)
{
printf("%f ", m.m_Data[i* m.m_M+j]);
}
printf("\n");
}
}
Matrix Matrix::mulptiple1(const Matrix& A, const Matrix& B)
{
Matrix C(A.m_N, B.m_M, 0.0f);
for (unsigned int i = 0; i < A.m_N; i++)
{
for (unsigned int j = 0; j < B.m_M; j++)
{
float sum = 0.0f;//整型和浮点型的存储结构不同,所以初始化不用float sum = 0
for (unsigned int k = 0; k < A.m_M; k++)
{
//sum += A.getElement(i, k) * B.getElement(k, j);
sum += A.m_Data[i * A.m_M + k] * B.m_Data[k * B.m_M + j];
}
C.m_Data[i * A.m_M + j] = sum;
}
}
return C;
}
float& Matrix::element(unsigned int i, unsigned int j)
{
return m_Data[(i - 1) * m_M + j - 1];
}
float& Matrix::operator()(unsigned int i, unsigned int j)
{
return m_Data[(i - 1) * m_M + j - 1];
}
Matrix operator*(const Matrix& A, const Matrix& B)
{
return Matrix::mulptiple1(A, B);
}
对改进版2可以继续改进的方向:
1.改进版2的矩阵乘法是很慢的,相比matlab差了至少1w倍,我们可以采取以下方式提速,cpu的并行指令,gpu的并行计算,但这些技术都太深入了。所以这次只做一个简单的优化,叫做缓存优化。缓存优化的原理是cpu访问存在缓存机制,如有一个100个元素的数组a,当访问到第a[50]的时候,cpu会预先读出50以后的若干个元素放在缓存中。所以读数组时,如果下标是连续的,跳跃的范围很小,就可以读缓存进而会提速。因为改进版2的矩阵乘法有三层for循环,版本1的k在最内层,版本2的i在最内层,版本3的j在最内层,用版本3时就可以实现缓存优化,对程序效率有所提高。
转载标题:C++11 标准 | 用 C++ 编写矩阵 转载地址:https://www.123yun.com/article/1294.html
vps云服务器搭建 服务器韩国vps vps13a 韩国 vps的服务器