====MVP级强滢请进 请帮忙用ET技术实现一个极其小的矩阵类,内有要求,不是很难,对我很难,感谢先

mynetpc 2004-11-21 01:47:15
要求用模板类型。要求实现[i][j],要求用ET方法实现+ 。 要求内存不泄漏
...全文
168 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
mynetpc 2004-11-26
  • 打赏
  • 举报
回复
yuanye2008 2004-11-26
  • 打赏
  • 举报
回复
满天都是小星星!
sandrowjw 2004-11-26
  • 打赏
  • 举报
回复
偶4来学习的,devcpp的库里面好像有valarray(矢量运算)的et实现,不过真的很麻烦:(
mynetpc 2004-11-25
  • 打赏
  • 举报
回复
就实数范围吧,不用考虑复数域了
mynetpc 2004-11-25
  • 打赏
  • 举报
回复
看来俺的问题够档次啊。 呵呵
哪位能给一短示例啊,比如用ET实现[i][j]和*
zhangfjj 2004-11-22
  • 打赏
  • 举报
回复
呵呵,五星级的强淫来了!

:)
蒋晟 2004-11-22
  • 打赏
  • 举报
回复
这个矩阵类库怎么看怎么像偶大二的作业……实数还简单,不知道ET怎么考虑虚数矩阵的……
xuzheng318 2004-11-22
  • 打赏
  • 举报
回复
哇,都是星星,照的我眼睛睁不开了
yjh1982 2004-11-22
  • 打赏
  • 举报
回复
要求内存不泄漏
.......................

搞笑了点吧....
goodluckyxl 2004-11-22
  • 打赏
  • 举报
回复
偶是来看看MVP及人物的回答的

顺便来学习...

Andy84920 2004-11-21
  • 打赏
  • 举报
回复
转自:http://www.allaboutprogram.com/content/view/26/31/
简单介绍一下 Expression Template
属于作者: elminster
在 C++ 那几种渐渐为人们所熟悉的“奇技淫巧”之中,ET 可能是最具实用价值的技术之一了。借着它的帮助,人们可以构造出兼具便利和高效的通用库来。那么 ET 究竟是什么呢?笼统地说,ET 就是利用类型信息来储存表达式结构的技术。按照这个说法,人们通常所说的 lambda 表达式也可以划入 ET 技术的范畴,不过这里我主要以 ET 在矩阵运算方面的应用为例,lambda 表达式我会另文加以介绍。
这篇文章我来介绍一下 Express Template(以下简称 ET)。
在 C++ 那几种渐渐为人们所熟悉的“奇技淫巧”之中,ET 可能是最具实用价值的技术之一了。借着它的帮助,人们可以构造出兼具便利和高效的通用库来。那么 ET 究竟是什么呢?笼统地说,ET 就是利用类型信息来储存表达式结构的技术。按照这个说法,人们通常所说的 lambda 表达式也可以划入 ET 技术的范畴,不过这里我主要以 ET 在矩阵运算方面的应用为例,lambda 表达式我会另文加以介绍。
一、动机
按照常规,当我们学习线性代数的时候,矩阵运算是用代数式子给出的。因此,很自然的,当我们在计算机上编写程序的时候,我们希望所使用的矩阵库可以支持代数化的语法(为了便于讨论,假设 m1、m2、m3、m4 是四个 N 维的整数方阵,下同):
== <code1> ================================
matrix m1, m2, m3, m4;
... ...

// 我们希望这样
m1 = m2 + m3 * m4;

// 而不是这样
mul(m1, m3, m4);
add(m1, m1, m2);
== </code1> ===============================
对于 C++ 来说,要实现一个库支持这样的语法非常简单,我们只要书写一个类 matrix 并为之重载相应的运算符就行了,像这样:
== <code2> ================================
class matrix
{
// ... ...
int content[N][N];

public:
int get_value(int i, int j) const { return content[i][j]; }

const matrix& operator=(const matrix& other)
{
if( this == &other )
return *this;

for(int i=0; i<N; ++i)
for(int j=0; j<N; ++j)
content[i][j] = other.content[i][j];
}
friend matrix operator+(const matrix& lhs, const matrix& rhs)
{
matrix tmp;
for(int i=0; i<N; ++i)
for(int j=0; j<N; ++j)
tmp.content[i][j] = lhs.content[i][j] + rhs.content[i][j];
}

// ... ... 乘法类似实现,不赘
};
== </code2> ===============================

经过一番小小的努力,我们就能够使用像 code1 那样的语法了。然而这样的实现方式,对于学习 C++ 语言的朋友来说或许是一个不错的练习,但若要将之投入实用,却是让人根本无法接受的。运算符重载只是语法糖,漂亮的语法下面,其实质是嵌套的函数调用,稍加分析就可以发现,上述实现方式在支持代数化的语法的同时,在效率方面付出了巨大代价。以 code1 中的表达式为例:

== <code3> ================================

// 这个表达式
m1 = m2 + m3 * m4;

// 实质是
m1.opeartor=( operator+( m2, opeartor*(m3, m4) ) );

== </code3> ===============================

结合上面的代码,我们可以看到当调用 operator* 计算 m3*m4 的时候,我们先构造了一个矩阵 tmp ,然后执行乘法运算计算其内容,接着当函数返回时,tmp 析构,而返回值是一个临时对象,它必须从 tmp 拷贝构造并在恰当的时刻析构。在这个过程中,除了实际所需运算外,我们得多做一次构造,一次拷贝构造和两次析构 —— 光是一次拷贝构造,就意味着要把 N^2 个数挨个复制一遍,其他运算符差不多也是如此。可以预见,我们使用的表达式越长、越复杂,这些开销就越大,最终可能导致程序的执行效率下降一个数量级。

这显然是无法令人接受的,因此,长久以来人们依然只能使用 add/mul 这样怎么看都像汇编指令的语法,Fortran 也依然大行其道。但借着 ET 的帮助,事情就不同了:我们仍然可以写 m1 = m2 + m3 * m4; ,同时又不损及效率。

这当然很 cool,可它是怎么干的呢?

二、技术

从前面的分析可以知道,我们的时间其实主要都浪费在构造、保存和复制中间结果上了,要提高效率,必须设法消除这些中间结果。对此,ET 的答案是:将整个表达式的结构编码入类型信息,借此将实际计算延迟到实际需要之时,也就是执行赋值操作的时候。唔,这么说实在太笼统,还是让我们来看看代码,看看它是怎么做的:

== <code4> ================================

// matrix 实现
class matrix
{
// ... ...
int content[N][N];

public:
int get_value(int i, int j) const { return content[i][j]; }

template<class E>
const matrix& operator=(const E& expr)
{
for(int i=0; i<N; ++i)
for(int j=0; j<N; ++j)
content[i][j] = expr.get_value(i, j);

return *this;
}
};

== </code4> ===============================

首先是 matrix 。这里 matrix::operator= 变成了一个模板函数,而且我调整了参数的命名,指明传入的参数 expr 是一个表达式,它的类型是模板参数 E。这里使用模板是必要的,因为 expr 这个表达式的结构信息保存在它的类型之内,不同的表达式就有不同的类型 —— 但所有的表达式都可以通过 get_value(i, j) 来获得这个表达式运算结果中的各个元素。接下来的问题就是怎样把表达式的结构放进类型信息了。简单起见,这里只展示加法的具体实现:

== <code5> ================================

// 加法实现
template<class L, class R>
class expr_add
{
const L& lhs;
const R& rhs;

public:
expr_add(const L& l, const R& r) : lhs(l), rhs(r) {}

int get_value(int i, int j) const
{
return lhs.get_value(i, j) + rhs.get_value(i, j);
}
};

template<class L, class R> inline
expr_add<L, R> operator+(const L& lhs, const R& rhs)
{
return expr_add<L, R>(lhs, rhs);
}

== </code5> ===============================

上面这种手法是 ET 区别于其他技术的地方。在这里,operator+ 这个重载的运算符并不实际完成加法操作,而是返回一个保存了表达式有关信息的对象,expr_add 。expr_add 这个类模板代表加法表达式,它的模板参数 L 和 R 分别代表了这个加法表达式左子表达式和右子表达式的类型,并使用两个引用成员 lhs 和 rhs 分别保存实际的左右子表达式。现在让我们来看看当我们在程序写下 m1 = m2 + m3 + m4 + m5; 的时候会发生什么:

== <code6> ================================

// 按照运算符重载的规则:

m1 = m2 + m3 + m4 + m5;

// 等价于:

m1.operator=( operator+( operator+( operator+(m2, m3), m4 ), m5 );

// 让我们把等号右边的这一串函数嵌套调用展开,看看各级的返回类型是什么
// (matrix 简写为 M)

m1.operator=( operator+( operator+( operator+(m2, m3), m4 ), m5 );
// ~~~~~~~~~~~
// expr_add<M, M>
// ~~~~~~~~~~~~~~~~~~~~~~
// expr_add< expr_add<M, M>, M>
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// expr_add< expr_add< expr_add<M, M>, M>, M>

== </code6> ===============================

可以看到,expr_add 的两个模板参数就像二叉树一样将整个表达式的结构递归地保存在类型信息之内,等到我们在 operator= 里面取 expr.get_value(i, j); 的时候,由于这一函数的 inline 性质,这个调用可以其逐级展开:

== <code7> ================================

// 下面这个调用:

content[i][j] = expr.get_value(i, j);

// 我们可以这样展开:

// expr_add< expr_add< expr_add<M, M>, M >, M >::get_value
// 展开为:
// expr_add< expr_add<M, M>, M >::get_value + M::get_value
// 展开为:
// expr_add<M, M>::get_value + M::get_value + M::get_value
// 展开为:
// M::get_value + M::get_value + M::get_value + M::get_value
// 因此,最终我们可以得到
content[i][j] = m2.get_value(i, j) + m3.get_value(i,j) + m4.get_value(i, j) + m5.get_value(i,j);


还未帖完,放不下.所以请看原址.
Andy84920 2004-11-21
  • 打赏
  • 举报
回复
但是如果你需要我把所有的都完整的写出来,....咦....不是偶滴作业啊.^_^

等有时间再看看.
Andy84920 2004-11-21
  • 打赏
  • 举报
回复
这个不需要MVP级人物写.

64,639

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧