c++:重载操作符的后续操作
C++: about subsequent operations of overloaded operator
#include <iostream>
#include <vector>
using namespace std;
void testfn(double* v1, double *v2, double *v3, int n);//I must use this function
class CLS{
private:
vector<double> v;
public:
CLS(vector<double> vin);
CLS operator+(CLS & A);
CLS operator*(CLS & A);
};
CLS::CLS(vector<double> vin)
{
v=vin;
}
CLS CLS::operator*(CLS &A){
//assuming the two vectors have the same length
vector<double> vtmp(v.size(),0);
testfn(&*A.v.begin(),&*v.begin(),&*vtmp.begin(),(int)A.v.size());
CLS C(vtmp);
return C;
}
CLS CLS::operator+(CLS &A){
//assuming the two vectors have the same length
vector<double> vtmp(v.size(),0);
testfn(&*A.v.begin(),&*v.begin(),&*vtmp.begin(),(int)A.v.size());
CLS C(vtmp);
return C;
}
void testfn(double* v1, double *v2, double *v3, int n)
{
for (int i=0;i<n;i++)
{
*v3=*v1+*v2;
++v1;
++v2;
++v3;
}
}
int main(){
vector<double> v1(100,1.0), v2(100,2.0), v3(100,0.0);
CLS C1(v1),C2(v2),C3(v3);
CLS C4=C1*(C1+(C2*C3+C2))*C1*C2*C3+C1+C2+C3;
return 0;
}
我创建了类CLS并定义了两个操作符+和*。我想使用这些操作符,就像我们对整数和双精度浮点数使用+和*一样简单。因此,我在主CLS C4=C1*(C1+(C2*C3+C2))*C1*C2*C3+C1+C2+C3;
中有一条测试线。然而,在使用gcc编译这段代码时,我得到了大量的错误。我对操作符重载的规则不太熟悉。我应该如何修改*和+的定义(也许只是参数?),使CLS C4=C1*(C1+(C2*C3+C2))*C1*C2*C3+C1+C2+C3;
是有效的?
补充:我不知道操作符+和*的const形参如何使用,因为这两个操作符的定义涉及到另一个函数testfn
,它的形参是double*而不是const double*。此外,我不想改变testfn
定义中的任何部分,因为在我的实际代码中,它对应于LAPACK中的一个函数,我绝对没有权利改变它。
最好将operator+
和operator*
定义为友元函数,这样,如果存在从其他类型到CLS
的隐式转换,则可以使用操作符。如果它是一个成员函数,那么如果第一个操作数不是CLS
,它将找不到。
friend CLS operator+(CLS lhs, const CLS& rhs)
{
// Do your logic here
return lhs;
}
你应该在你的代码中使用空格,这是可怕的阅读。不要在类型名和变量中都使用大写字母,那样读起来也很可怕。
这不是必需的,但这是一个小的优化,通过引用传递构造函数参数,然后在构造函数init列表中初始化v
成员(而不是在主体中对其赋值):
CLS::CLS(const vector<double>& vin) : v(vin)
{
}
我将添加一个新的构造函数,它接受所需的大小,并立即将v
成员的大小设置为该大小,这使下面的代码更简单:
CLS::CLS(int n) : v(n, 0)
{
}
首先将const
添加到函数形参和成员函数本身:
CLS CLS::operator*(const CLS& a) const {
^^^^^ ^^^^^
然后为结果默认构造一个空的C
对象,具有所需大小的vector成员:
//assuming the two vectors have the same length
CLS result(v.size());
如果创建了数据向量的副本,并对这些副本进行操作,则可以使成员函数及其实参为const:
vector<double> v1 = a.v;
vector<double> v2 = v;
现在你可以获得指向这些向量中的数据的非const指针,并且你可以直接将输出写入C.v
,而不是写入vtmp
,然后将其复制到返回的对象中:
testfn(&*v1.begin(), &*v2.begin(), &*result.v.begin(), v.size());
最后返回结果:
return result;
}
另一个函数的变化完全相同:
CLS CLS::operator+(const CLS& a) const {
//assuming the two vectors have the same length
CLS result(v.size());
vector<double> v1 = a.v;
vector<double> v2 = v;
testfn(&*v1.begin(), &*v2.begin(), &*result.v.begin(), v.size());
return result;
}
现在这将能够接受const参数,因此应该与每个C1+C2
或C3*C4
表达式创建的临时对象一起工作。
由于数据的额外副本,代码的效率略低,但构造函数中的优化有助于此。如果你能够使用c++ 11,即使这些低效率也可以解决,但需要更多的工作。
首先,c++ 11提供了vector<T>::data()
来获取指针,这比&*v.begin()
要清晰得多。(在c++ 11之前,它不是c++标准的一部分,但GCC甚至在c++ 98中提供了它)。您可以将上面显示的代码更改为使用v.data()
而不是&*v.begin()
,如下所示。
在c++ 11中,可以通过将(非const) 右值引用绑定到临时对象,从而为临时对象添加额外的重载。因为右值引用允许对临时对象进行非const访问,所以不需要复制它们的数据来获取非const指针,而是可以直接使用它们。除了上面所示的操作符重载之外,还可以添加这些操作符重载:
CLS CLS::operator+(CLS&& a) && {
//assuming the two vectors have the same length
CLS result(v.size());
testfn(a.v.data(), v.data(), result.v.data(), v.size());
return result;
}
CLS CLS::operator*(CLS&& a) && {
//assuming the two vectors have the same length
CLS result(v.size());
testfn(a.v.data(), v.data(), result.v.data(), v.size());
return result;
}
这避免了在算术表达式创建的中间临时对象中进行任何复制。我还发现更容易理解,更容易阅读。
一个完整的工作示例在http://coliru.stacked-crooked.com/a/6e40d20eb7f1e9fc使用testfn
与所有非const参数的不寻常要求有点奇怪,但它是…嗯. .勉强接受对于复杂表达式不接受——testfn的前两个参数必须是const double*
。
基本原理: c++标准要求临时绑定到const references
-这里有一些有趣的细节
@Resorter说:
"谢谢你让我知道,我想学习。但是你确定它能解决我的问题吗?我真的想先解决这个问题。"
[where it is 'move constructor' and 'move assign ']
下面的"fish"假设你已经阅读/学习了fishing move constructor和move assign以及5/3/0的规则,
补充学习材料:拷贝省略-当编译器被允许跳过拷贝/移动构造函数时,即使它们会产生副作用。
// some implementations
void testfn0(const double* v1, const double *v2, double *v3, int n) {
for(int i=0; i<n; i++) {
v3[i]=v1[i]+v2[i];
}
}
void testfn1(double* v1, double *v2, double *v3, int n) {
for(int i=0; i<n; i++) {
v3[i]=v1[i]*v2[i];
}
}
class CLS {
std::vector<double> v;
public:
// constructor taking a const reference
CLS(const std::vector<double>& in) : v(in) {
std::cout << "copy vec" << std::endl;
}
// extra constructor with rvalue reference: it will "canibalize" the parameter
CLS(std::vector<double>&& in) : v(in) {
std::cout << "move vec" << std::endl;
}
// Rule of 5 applied
// Copy constructor
CLS(const CLS& other) : v(other.v) {
std::cout << "copy CLS" << std::endl;
}
// Move constructor
CLS(CLS&& other) : v(std::move(other.v)) {
std::cout << "move CLS" << std::endl;
}
~CLS() { }
// Copy assgn
CLS& operator=(const CLS& o) {
this->v=o.v;
std::cout << "assgn CLS" << std::endl;
return *this;
}
// Move assgn
CLS& operator=(CLS&& other) {
this->v=std::move(other.v);
std::cout << "move CLS" << std::endl;
return *this;
}
// WILL NOT WORK WITHOUT const FOR COMPLEX EXPRESSIONS
// |
// ----------
// V
CLS operator+(const CLS& rhs) {
std::vector<double> vtmp(v.size(),0);
testfn0(&*rhs.v.begin(),&*v.begin(),&*vtmp.begin(),(int)rhs.v.size());
CLS ret(std::move(vtmp));
return ret;
}
// WILL NOT WORK WITHOUT const FOR COMPLEX EXPRESSIONS
// |
// ----------
// V
CLS operator*(const CLS& rhs) {
std::vector<double> vtmp(v.size(),0);
testfn1(&*rhs.v.begin(),&*v.begin(),&*vtmp.begin(),(int)rhs.v.size());
CLS ret(std::move(vtmp));
return ret;
}
};
int main() {
std::cout << "n--- inits " << std::endl;
CLS a(std::vector<double>{1,2,3}); // move vec
CLS b(std::vector<double>{3,2,1}); // move vec
std::cout << "n--- add " << std::endl;
CLS c=a+b; // move vec and 'silence' (copy elision)
std::cout << "n--- mul " << std::endl;
CLS d=a*b; // move vec and 'silence' (copy elision)
std::cout << "n--- copy " << std::endl;
CLS m=c; // Copy CLS - constructor
std::cout << "n--- move " << std::endl;
CLS n=std::move(d); // Move CLS - constructor
std::cout << "n--- assgn (d=c) and copy (x=c)" << std::endl;
CLS x=d=c;
}
- 为什么在popback()操作之后,它仍然打印完整的矢量
- 重载操作程序时出错>>用于类中的字符串 memebr
- 对字符串进行位操作
- 我可以在 C++ 中的函数体之外进行操作吗?
- MPI突然停止了对多个核心的操作
- 如何在信号处理程序和普通函数中对全局变量进行互斥读写操作
- 对字符数组中的元素执行逐位操作
- 如何在directx/c++中进行平移/缩放操作
- 逐位操作的隐式类型转换
- 为什么一个向量上的多线程操作很慢
- 排序时无法执行交换操作.我做的时候它会崩溃.为什么
- 位移操作和位掩码未检测到重复字符
- 如何进行特定的位操作?
- 当我们进行一些操作时,应该使用什么'std::string'或'std::stringstream'?
- 字符串操作 - 字符计数
- 此代码中的操作流程是什么?C/C++.
- 使用重载操作符的文件操作表达式没有给出预期的结果
- c++:重载操作符的后续操作
- 如何声明操作符/重载函数来操作const变量和非const变量
- c++如何通过操作符[]进行写操作