class Matrix
{
private:
int rows;
int cols;
public:
Matrix & operator *(int n);
friend Matrix & operator *(int n,Matrix &mat);
};
Matrix &operator *(int n,Matrix &mat)
{
return (mat*n);
}
问题:
1.这里的乘号操作符为什么要声明为友元,声明为成员函数不行么?声明为友元仅仅是为了使用两个参数?
2.这种友元调用不就是5.operator*(mat)么? 那么这种声明会不会重载了整型的乘号操作符?
3.编译器能自动识别在这个友元中的这些参数的前后位置吗?把n 与mat的声明位置换一下可以么?
27 个解决方案
#1
1. 可以声明为成员函数。
2. 这种友无调用就是5.operaor*(mat),不会重载整型的乘号操作符。
3. 编译能自动识别在这个友元中的这些参数的前后位置。不可以。
2. 这种友无调用就是5.operaor*(mat),不会重载整型的乘号操作符。
3. 编译能自动识别在这个友元中的这些参数的前后位置。不可以。
#2
1.声明成友元使函数和类的成员函数具有相同的权限,能存取类的私有成员。
3.编译器不能自动识别,除非自己写它的相关的重栽形式。
3.编译器不能自动识别,除非自己写它的相关的重栽形式。
#3
重载运算符的时候,要求运算符的左边必须为类本身(如重载<<)如果要在右边用类的话就必须,申明成友元,友元的存取权限和本身曾员一样
#4
1。这里声明为友元函数的最大好处就是可以采用将内建型别作为*号的第一参数(左参)从而达到与内建型别运算规则完全一致的目的。i.e.当你不声明friend,而只是一个成员函数的话,就不能使用5*mat;
2。不是对整型*号的重载。C++不同于Java,没有类似Java的Integer这样的外包类,所以对于C++内建型别(注意:它不是类,没有对象)来说也不存在什么重载了。
3。编译器不能自动识别,如果按照你对*号两个参数的重载,其意义很明确:第一参表示乘号左边的操作数,第二参表示右边的操作数。
2。不是对整型*号的重载。C++不同于Java,没有类似Java的Integer这样的外包类,所以对于C++内建型别(注意:它不是类,没有对象)来说也不存在什么重载了。
3。编译器不能自动识别,如果按照你对*号两个参数的重载,其意义很明确:第一参表示乘号左边的操作数,第二参表示右边的操作数。
#5
用了友员就没有函数的参数的左右之分
3。编译器不能自动识别,如果按照你对*号两个参数的重载,其意义很明确:第一参表示乘号左边的操作数,第二参表示右边的操作数。???????????????(好象是不对~)
3。编译器不能自动识别,如果按照你对*号两个参数的重载,其意义很明确:第一参表示乘号左边的操作数,第二参表示右边的操作数。???????????????(好象是不对~)
#6
呵呵。好像大家的说法不太一致...
#7
to bshaozi(俺们那噶不学c!)
RookieStar(★)(巴巴罗萨) 是对的
对于你认为的那种不分左右的情况,实际上是由于类存在一个构造函数可以让编译器进行隐式转换
对于本例,如果Matrix有一个构造函数Matrix(int)的话,那么它定义一个友元
friend Matrix& operator*(const Matrix& lhs, const Matrix& rhs)就可以胜任以下情况:
Matrix * Matrix
Matrix * int
int * Matrix
RookieStar(★)(巴巴罗萨) 是对的
对于你认为的那种不分左右的情况,实际上是由于类存在一个构造函数可以让编译器进行隐式转换
对于本例,如果Matrix有一个构造函数Matrix(int)的话,那么它定义一个友元
friend Matrix& operator*(const Matrix& lhs, const Matrix& rhs)就可以胜任以下情况:
Matrix * Matrix
Matrix * int
int * Matrix
#8
呵呵,心宇兄说的好!
#9
guifuli(一览众山小) 回答得很清楚
有关友元参数对调做点补充说明:
根据你定义的情况,是不可以的,但是如果你的成员函数没有operator *(int)的话,是可以对掉的,这样一来,你就需要定义2个友元操作符了。
有关友元参数对调做点补充说明:
根据你定义的情况,是不可以的,但是如果你的成员函数没有operator *(int)的话,是可以对掉的,这样一来,你就需要定义2个友元操作符了。
#10
1. 操作符出现在非本类右边(后面),必须用有员,以使得它类可以使用本类的私有成员(当然,如果你所有的都是public,可以例外);
2. 正如你所说,确实重载了int 的*操作符,比如我们经常需要重载ostream类的操作符<<
3. 编译器可以识别。
4. friend Matrix & operator *(int n,Matrix &mat);这种方式是有些问题的,这样更好:
Matrix operator * (const int, const Matrix&); 并且需要实现Matrix类的拷贝构造函数:
Matrix(const Matrix&); 以及重载
Matrix & operator = (const Matrix&);
按惯例,实现这样的乘法之前应该先实现重载操作符*=
Matrix & operator *= (const int);
这是因为这样的乘法需要返回临时变量所致。
2. 正如你所说,确实重载了int 的*操作符,比如我们经常需要重载ostream类的操作符<<
3. 编译器可以识别。
4. friend Matrix & operator *(int n,Matrix &mat);这种方式是有些问题的,这样更好:
Matrix operator * (const int, const Matrix&); 并且需要实现Matrix类的拷贝构造函数:
Matrix(const Matrix&); 以及重载
Matrix & operator = (const Matrix&);
按惯例,实现这样的乘法之前应该先实现重载操作符*=
Matrix & operator *= (const int);
这是因为这样的乘法需要返回临时变量所致。
#11
可以参考一下:
class matrix
{
public:
matrix(const int, const int);
matrix(const matrix&);
~matrix(void);
double & operator()(const int, const int);
matrix & operator = (const matrix&);
matrix & operator = (const double *);
matrix & operator += (const matrix&);
matrix operator + (const matrix&);
matrix & operator -= (const matrix&);
matrix operator - (const matrix&);
matrix & operator *= (const double&);
matrix operator * (const double&);
friend matrix operator * (const double&, const matrix&);
friend ostream & operator << (ostream &, const matrix&);
int resize(const int, const int);
private:
int setmemsize(const int, const int);
int L_X, L_Y;
double *data;
};
int matrix::setmemsize(const int x, const int y)
{
L_X = x;
L_Y = y;
data = new double[x*y];
memset(data, 0, x * y * sizeof(double));
return 0;
}
matrix::matrix(const int x, const int y)
{
setmemsize(x,y);
}
matrix::matrix(const matrix& m)
{
setmemsize(m.L_X, m.L_Y);
memcpy(data, m.data, L_X * L_Y * sizeof(double));
}
matrix::~matrix(void)
{
delete[]data;
}
double & matrix::operator () (const int x, const int y)
{
return *(data + y *L_X + x);
}
ostream & operator << (ostream & os, const matrix& m)
{
double *d = m.data;
for (int y = 0; y < m.L_Y; ++y)
for (int x = 0; x < m.L_X; ++x,++d) os<<*d<<'\t';
return os;
}
int matrix::resize(const int x, const int y)
{
delete[]data;
return setmemsize(x,y);
}
matrix & matrix::operator = (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
memcpy(data, m.data, L_X * L_Y * sizeof(double));
return *this;
}
matrix & matrix::operator = (const double *d)
{
memcpy(data, d, L_X * L_Y * sizeof(double));
return *this;
}
matrix & matrix::operator += (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
int size = L_X * L_Y;
double* m1 = data;
double* m2 = m.data;
for(; size--; *(m1++)+=*(m2++));
return *this;
}
matrix matrix::operator + (const matrix& m)
{
matrix temp(L_X, L_Y);
if ((L_X != m.L_X) || (L_Y != m.L_Y)) return matrix(temp);
temp = *this;
temp += m;
return matrix(temp);
}
matrix & matrix::operator -= (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
int size = L_X * L_Y;
double* m1 = data;
double* m2 = m.data;
for(; size--; *(m1++)-=*(m2++));
return *this;
}
matrix matrix::operator - (const matrix& m)
{
matrix temp(L_X, L_Y);
if ((L_X != m.L_X) || (L_Y != m.L_Y)) return matrix(temp);
temp = *this;
temp -= m;
return matrix(temp);
}
matrix & matrix::operator *= (const double& d)
{
int size = L_X * L_Y;
double* m = data;
for(; size--; *(m++) -= d);
return *this;
}
matrix matrix::operator * (const double& d)
{
matrix temp(*this);
temp *= d;
return matrix(temp);
}
matrix operator * (const double& d, const matrix& m)
{
matrix temp(m);
temp *= d;
return matrix(temp);
}
class matrix
{
public:
matrix(const int, const int);
matrix(const matrix&);
~matrix(void);
double & operator()(const int, const int);
matrix & operator = (const matrix&);
matrix & operator = (const double *);
matrix & operator += (const matrix&);
matrix operator + (const matrix&);
matrix & operator -= (const matrix&);
matrix operator - (const matrix&);
matrix & operator *= (const double&);
matrix operator * (const double&);
friend matrix operator * (const double&, const matrix&);
friend ostream & operator << (ostream &, const matrix&);
int resize(const int, const int);
private:
int setmemsize(const int, const int);
int L_X, L_Y;
double *data;
};
int matrix::setmemsize(const int x, const int y)
{
L_X = x;
L_Y = y;
data = new double[x*y];
memset(data, 0, x * y * sizeof(double));
return 0;
}
matrix::matrix(const int x, const int y)
{
setmemsize(x,y);
}
matrix::matrix(const matrix& m)
{
setmemsize(m.L_X, m.L_Y);
memcpy(data, m.data, L_X * L_Y * sizeof(double));
}
matrix::~matrix(void)
{
delete[]data;
}
double & matrix::operator () (const int x, const int y)
{
return *(data + y *L_X + x);
}
ostream & operator << (ostream & os, const matrix& m)
{
double *d = m.data;
for (int y = 0; y < m.L_Y; ++y)
for (int x = 0; x < m.L_X; ++x,++d) os<<*d<<'\t';
return os;
}
int matrix::resize(const int x, const int y)
{
delete[]data;
return setmemsize(x,y);
}
matrix & matrix::operator = (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
memcpy(data, m.data, L_X * L_Y * sizeof(double));
return *this;
}
matrix & matrix::operator = (const double *d)
{
memcpy(data, d, L_X * L_Y * sizeof(double));
return *this;
}
matrix & matrix::operator += (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
int size = L_X * L_Y;
double* m1 = data;
double* m2 = m.data;
for(; size--; *(m1++)+=*(m2++));
return *this;
}
matrix matrix::operator + (const matrix& m)
{
matrix temp(L_X, L_Y);
if ((L_X != m.L_X) || (L_Y != m.L_Y)) return matrix(temp);
temp = *this;
temp += m;
return matrix(temp);
}
matrix & matrix::operator -= (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
int size = L_X * L_Y;
double* m1 = data;
double* m2 = m.data;
for(; size--; *(m1++)-=*(m2++));
return *this;
}
matrix matrix::operator - (const matrix& m)
{
matrix temp(L_X, L_Y);
if ((L_X != m.L_X) || (L_Y != m.L_Y)) return matrix(temp);
temp = *this;
temp -= m;
return matrix(temp);
}
matrix & matrix::operator *= (const double& d)
{
int size = L_X * L_Y;
double* m = data;
for(; size--; *(m++) -= d);
return *this;
}
matrix matrix::operator * (const double& d)
{
matrix temp(*this);
temp *= d;
return matrix(temp);
}
matrix operator * (const double& d, const matrix& m)
{
matrix temp(m);
temp *= d;
return matrix(temp);
}
#12
我的看法:这个地方不声明为friend完全没有问题。声明为friend对于参数传递、匹配什么的,根本没有影响,只不过friend可以存取把它声明为friend的类的private成员了,亦即,完全是访问控制的问题。可是这里,在全局函数Matrix &operator *(int n,Matrix &mat)中,根本没有牵涉到Matrix类的private成员的访问,可以认为它只是一个辅助的函数(这样Matrix类的interface可以“减肥”一点,B.S.教导我们,类的interface不要过于fat)。不信的话大家可以试一试,在这里不把这个全局的*操作符重载函数声明为Matrix的friend,完全没有问题。
#13
基本上可以看出,写出这个class的人应该水平不错.
但是大部分的问答我感觉都有些错误.
1.这里的乘号操作符为什么要声明为友元,声明为成员函数不行么?声明为友元仅仅是为了使用两个参数?
对于你这种特定的情况来说,声明友元主要是一种习惯,因为现在你的实现并没有用到非public的东西,所以去掉也可以.angelo23(angelo)说的不错. 除了惯例以外,还有一个原因是,如果以后你修改了这个全局函数的定义,例如需要使用某些private的成员时,你不需要去修改Matrix类里面的东西了.
另外,声明成成员函数是不行的,它就无法实现 5 * matrix 这样的语句.
2.这种友元调用不就是5.operator*(mat)么? 那么这种声明会不会重载了整型的乘号操作符?
这种友元不是调用5.operator*(mat). int已经是内置的类型,并没有定义这样的操作(也不可能,你难道期望C++能够预见到某天有人会写出一个Matrix类吗?)因此也就没有重载int的*操作符. C++的编译器在遇到x * y的时候,它会查找定义在X类中的成员函数 X::operator*(Y y), 如果没有找到,那么就在全局范围内查找 operator *(X x, Y y)或者使用引用的版本. 结果就匹配上了刚刚定义的Matrix& operator *(int n,Matrix &mat)
3.编译器能自动识别在这个友元中的这些参数的前后位置吗?把n 与mat的声明位置换一下可以么?
不太明白你的第一个问题. 位置不能换.现在的Matrix 的定义已经比较完整了.
例如我们调用 5 * matrix, 那么就调用全局范围内的
Matrix& operator *(int n,Matrix &mat)
如果调用 matrix * 5, 那么就调用Matrix类里面定义的
Matrix& Matrix::opreator *(int x)
如果你调整了次序,将全局的那个改为Matrix& operator *(Matrix &mat, int n), 那么两个都是期望Matrix的类型在左边, 也就是仅仅支持 Matrix * 5, 而无法支持 5 * matrix 了.
另外,楼上某些说的构造函数隐式转换,在你的定义中看不出来,也和你问的问题关系不大.
但是大部分的问答我感觉都有些错误.
1.这里的乘号操作符为什么要声明为友元,声明为成员函数不行么?声明为友元仅仅是为了使用两个参数?
对于你这种特定的情况来说,声明友元主要是一种习惯,因为现在你的实现并没有用到非public的东西,所以去掉也可以.angelo23(angelo)说的不错. 除了惯例以外,还有一个原因是,如果以后你修改了这个全局函数的定义,例如需要使用某些private的成员时,你不需要去修改Matrix类里面的东西了.
另外,声明成成员函数是不行的,它就无法实现 5 * matrix 这样的语句.
2.这种友元调用不就是5.operator*(mat)么? 那么这种声明会不会重载了整型的乘号操作符?
这种友元不是调用5.operator*(mat). int已经是内置的类型,并没有定义这样的操作(也不可能,你难道期望C++能够预见到某天有人会写出一个Matrix类吗?)因此也就没有重载int的*操作符. C++的编译器在遇到x * y的时候,它会查找定义在X类中的成员函数 X::operator*(Y y), 如果没有找到,那么就在全局范围内查找 operator *(X x, Y y)或者使用引用的版本. 结果就匹配上了刚刚定义的Matrix& operator *(int n,Matrix &mat)
3.编译器能自动识别在这个友元中的这些参数的前后位置吗?把n 与mat的声明位置换一下可以么?
不太明白你的第一个问题. 位置不能换.现在的Matrix 的定义已经比较完整了.
例如我们调用 5 * matrix, 那么就调用全局范围内的
Matrix& operator *(int n,Matrix &mat)
如果调用 matrix * 5, 那么就调用Matrix类里面定义的
Matrix& Matrix::opreator *(int x)
如果你调整了次序,将全局的那个改为Matrix& operator *(Matrix &mat, int n), 那么两个都是期望Matrix的类型在左边, 也就是仅仅支持 Matrix * 5, 而无法支持 5 * matrix 了.
另外,楼上某些说的构造函数隐式转换,在你的定义中看不出来,也和你问的问题关系不大.
#14
thinking in c++的重载那一部分应该由相关描述
#15
好多高手,学习学习!
#16
看来这个帖子一定要有重量级人物登场才能结贴,以下载自Scott Meyers《Effective C++》算是对我的回答的补充以及大家讨论的总结 :P
条款19: 分清成员函数,非成员函数和友元函数
成员函数和非成员函数最大的区别在于成员函数可以是虚拟的而非成员函数不行。所以,如果有个函数必须进行动态绑定(见条款38),就要采用虚拟函数,而虚拟函数必定是某个类的成员函数。关于这一点就这么简单。如果函数不必是虚拟的,情况就稍微复杂一点。
看下面表示有理数的一个类:
class rational {
public:
rational(int numerator = 0, int denominator = 1);
int numerator() const;
int denominator() const;
private:
...
};
这是一个没有一点用处的类。(用条款18的术语来说,接口的确最小,但远不够完整。)所以,要对它增加加,减,乘等算术操作支持,但是,该用成员函数还是非成员函数,或者,非成员的友元函数来实现呢?
当拿不定主意的时候,用面向对象的方法来考虑!有理数的乘法是和rational类相联系的,所以,写一个成员函数把这个操作包到类中。
class rational {
public:
...
const rational operator*(const rational& rhs) const;
};
(如果你不明白为什么这个函数以这种方式声明——返回一个const值而取一个const的引用作为它的参数——参考条款21-23。)
现在可以很容易地对有理数进行乘法操作:
rational oneeighth(1, 8);
rational onehalf(1, 2);
rational result = onehalf * oneeighth; // 运行良好
result = result * oneeighth; // 运行良好
但不要满足,还要支持混合类型操作,比如,rational要能和int相乘。但当写下下面的代码时,只有一半工作:
result = onehalf * 2; // 运行良好
result = 2 * onehalf; // 出错!
这是一个不好的苗头。记得吗?乘法要满足交换律。
如果用下面的等价函数形式重写上面的两个例子,问题的原因就很明显了:
result = onehalf.operator*(2); // 运行良好
result = 2.operator*(onehalf); // 出错!
对象onehalf是一个包含operator*函数的类的实例,所以编译器调用了那个函数。而整数2没有相应的类,所以没有operator*成员函数。编译器还会去搜索一个可以象下面这样调用的非成员的operator*函数(即,在某个可见的名字空间里的operator*函数或全局的operator*函数):
result = operator*(2, onehalf); // 错误!
但没有这样一个参数为int和rational的非成员operator*函数,所以搜索失败。
再看看那个成功的调用。它的第二参数是整数2,然而rational::operator*期望的参数却是rational对象。怎么回事?为什么2在一个地方可以工作而另一个地方不行?
秘密在于隐式类型转换。编译器知道传的值是int而函数需要的是rational,但它也同时知道调用rational的构造函数将int转换成一个合适的rational,所以才有上面成功的调用(见条款m19)。换句话说,编译器处理这个调用时的情形类似下面这样:
const rational temp(2); // 从2产生一个临时
// rational对象
result = onehalf * temp; // 同onehalf.operator*(temp);
当然,只有所涉及的构造函数没有声明为explicit的情况下才会这样,因为explicit构造函数不能用于隐式转换,这正是explicit的含义。如果rational象下面这样定义:
class rational {
public:
explicit rational(int numerator = 0, // 此构造函数为
int denominator = 1); // explicit
...
const rational operator*(const rational& rhs) const;
...
};
那么,下面的语句都不能通过编译:
result = onehalf * 2; // 错误!
result = 2 * onehalf; // 错误!
这不会为混合运算提供支持,但至少两条语句的行为一致了。
然而,我们刚才研究的这个类是要设计成可以允许固定类型到rational的隐式转换的——这就是为什么rational的构造函数没有声明为explicit的原因。这样,编译器将执行必要的隐式转换使上面result的第一个赋值语句通过编译。实际上,如果需要的话,编译器会对每个函数的每个参数执行这种隐式类型转换。但它只对函数参数表中列出的参数进行转换,决不会对成员函数所在的对象(即,成员函数中的*this指针所对应的对象)进行转换。这就是为什么这个语句可以工作:
result = onehalf.operator*(2); // converts int -> rational
而这个语句不行:
result = 2.operator*(onehalf); // 不会转换
// int -> rational
第一种情形操作的是列在函数声明中的一个参数,而第二种情形不是。
尽管如此,你可能还是想支持混合型的算术操作,而实现的方法现在应该清楚了:使operator*成为一个非成员函数,从而允许编译器对所有的参数执行隐式类型转换:
class rational {
... // contains no operator*
};
// 在全局或某一名字空间声明,
// 参见条款m20了解为什么要这么做
const rational operator*(const rational& lhs,
const rational& rhs)
{
return rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
rational onefourth(1, 4);
rational result;
result = onefourth * 2; // 工作良好
result = 2 * onefourth; // 万岁, 它也工作了!
这当然是一个完美的结局,但还有一个担心:operator*应该成为rational类的友元吗?
这种情况下,答案是不必要。因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
然而,很多情况下,不是成员的函数从概念上说也可能是类接口的一部分,它们需要访问类的非公有成员的情况也不少。
让我们回头再来看看本书那个主要的例子,string类。如果想重载operator>>和operator<<来读写string对象,你会很快发现它们不能是成员函数。如果是成员函数的话,调用它们时就必须把string对象放在它们的左边:
// 一个不正确地将operator>>和
// operator<<作为成员函数的类
class string {
public:
string(const char *value);
...
istream& operator>>(istream& input);
ostream& operator<<(ostream& output);
private:
char *data;
};
string s;
s >> cin; // 合法, 但
// 有违常规
s << cout; // 同上
这会把别人弄糊涂。所以这些函数不能是成员函数。注意这种情况和前面的不同。这里的目标是自然的调用语法,前面关心的是隐式类型转换。
所以,如果来设计这些函数,就象这样:
istream& operator>>(istream& input, string& string)
{
delete [] string.data;
read from input into some memory, and make string.data
point to it
return input;
}
ostream& operator<<(ostream& output,
const string& string)
{
return output << string.data;
}
注意上面两个函数都要访问string类的data成员,而这个成员是私有(private)的。但我们已经知道,这个函数一定要是非成员函数。这样,就别无选择了:需要访问非公有成员的非成员函数只能是类的友元函数。
本条款得出的结论如下。假设f是想正确声明的函数,c是和它相关的类:
·虚函数必须是成员函数。如果f必须是虚函数,就让它成为c的成员函数。
·operator>>和operator<<决不能是成员函数。如果f是operator>>或operator<<,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
·只有非成员函数对最左边的参数进行类型转换。如果f需要对最左边的参数进行类型转换,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
·其它情况下都声明为成员函数。如果以上情况都不是,让f成为c的成员函数。
条款19: 分清成员函数,非成员函数和友元函数
成员函数和非成员函数最大的区别在于成员函数可以是虚拟的而非成员函数不行。所以,如果有个函数必须进行动态绑定(见条款38),就要采用虚拟函数,而虚拟函数必定是某个类的成员函数。关于这一点就这么简单。如果函数不必是虚拟的,情况就稍微复杂一点。
看下面表示有理数的一个类:
class rational {
public:
rational(int numerator = 0, int denominator = 1);
int numerator() const;
int denominator() const;
private:
...
};
这是一个没有一点用处的类。(用条款18的术语来说,接口的确最小,但远不够完整。)所以,要对它增加加,减,乘等算术操作支持,但是,该用成员函数还是非成员函数,或者,非成员的友元函数来实现呢?
当拿不定主意的时候,用面向对象的方法来考虑!有理数的乘法是和rational类相联系的,所以,写一个成员函数把这个操作包到类中。
class rational {
public:
...
const rational operator*(const rational& rhs) const;
};
(如果你不明白为什么这个函数以这种方式声明——返回一个const值而取一个const的引用作为它的参数——参考条款21-23。)
现在可以很容易地对有理数进行乘法操作:
rational oneeighth(1, 8);
rational onehalf(1, 2);
rational result = onehalf * oneeighth; // 运行良好
result = result * oneeighth; // 运行良好
但不要满足,还要支持混合类型操作,比如,rational要能和int相乘。但当写下下面的代码时,只有一半工作:
result = onehalf * 2; // 运行良好
result = 2 * onehalf; // 出错!
这是一个不好的苗头。记得吗?乘法要满足交换律。
如果用下面的等价函数形式重写上面的两个例子,问题的原因就很明显了:
result = onehalf.operator*(2); // 运行良好
result = 2.operator*(onehalf); // 出错!
对象onehalf是一个包含operator*函数的类的实例,所以编译器调用了那个函数。而整数2没有相应的类,所以没有operator*成员函数。编译器还会去搜索一个可以象下面这样调用的非成员的operator*函数(即,在某个可见的名字空间里的operator*函数或全局的operator*函数):
result = operator*(2, onehalf); // 错误!
但没有这样一个参数为int和rational的非成员operator*函数,所以搜索失败。
再看看那个成功的调用。它的第二参数是整数2,然而rational::operator*期望的参数却是rational对象。怎么回事?为什么2在一个地方可以工作而另一个地方不行?
秘密在于隐式类型转换。编译器知道传的值是int而函数需要的是rational,但它也同时知道调用rational的构造函数将int转换成一个合适的rational,所以才有上面成功的调用(见条款m19)。换句话说,编译器处理这个调用时的情形类似下面这样:
const rational temp(2); // 从2产生一个临时
// rational对象
result = onehalf * temp; // 同onehalf.operator*(temp);
当然,只有所涉及的构造函数没有声明为explicit的情况下才会这样,因为explicit构造函数不能用于隐式转换,这正是explicit的含义。如果rational象下面这样定义:
class rational {
public:
explicit rational(int numerator = 0, // 此构造函数为
int denominator = 1); // explicit
...
const rational operator*(const rational& rhs) const;
...
};
那么,下面的语句都不能通过编译:
result = onehalf * 2; // 错误!
result = 2 * onehalf; // 错误!
这不会为混合运算提供支持,但至少两条语句的行为一致了。
然而,我们刚才研究的这个类是要设计成可以允许固定类型到rational的隐式转换的——这就是为什么rational的构造函数没有声明为explicit的原因。这样,编译器将执行必要的隐式转换使上面result的第一个赋值语句通过编译。实际上,如果需要的话,编译器会对每个函数的每个参数执行这种隐式类型转换。但它只对函数参数表中列出的参数进行转换,决不会对成员函数所在的对象(即,成员函数中的*this指针所对应的对象)进行转换。这就是为什么这个语句可以工作:
result = onehalf.operator*(2); // converts int -> rational
而这个语句不行:
result = 2.operator*(onehalf); // 不会转换
// int -> rational
第一种情形操作的是列在函数声明中的一个参数,而第二种情形不是。
尽管如此,你可能还是想支持混合型的算术操作,而实现的方法现在应该清楚了:使operator*成为一个非成员函数,从而允许编译器对所有的参数执行隐式类型转换:
class rational {
... // contains no operator*
};
// 在全局或某一名字空间声明,
// 参见条款m20了解为什么要这么做
const rational operator*(const rational& lhs,
const rational& rhs)
{
return rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
rational onefourth(1, 4);
rational result;
result = onefourth * 2; // 工作良好
result = 2 * onefourth; // 万岁, 它也工作了!
这当然是一个完美的结局,但还有一个担心:operator*应该成为rational类的友元吗?
这种情况下,答案是不必要。因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
然而,很多情况下,不是成员的函数从概念上说也可能是类接口的一部分,它们需要访问类的非公有成员的情况也不少。
让我们回头再来看看本书那个主要的例子,string类。如果想重载operator>>和operator<<来读写string对象,你会很快发现它们不能是成员函数。如果是成员函数的话,调用它们时就必须把string对象放在它们的左边:
// 一个不正确地将operator>>和
// operator<<作为成员函数的类
class string {
public:
string(const char *value);
...
istream& operator>>(istream& input);
ostream& operator<<(ostream& output);
private:
char *data;
};
string s;
s >> cin; // 合法, 但
// 有违常规
s << cout; // 同上
这会把别人弄糊涂。所以这些函数不能是成员函数。注意这种情况和前面的不同。这里的目标是自然的调用语法,前面关心的是隐式类型转换。
所以,如果来设计这些函数,就象这样:
istream& operator>>(istream& input, string& string)
{
delete [] string.data;
read from input into some memory, and make string.data
point to it
return input;
}
ostream& operator<<(ostream& output,
const string& string)
{
return output << string.data;
}
注意上面两个函数都要访问string类的data成员,而这个成员是私有(private)的。但我们已经知道,这个函数一定要是非成员函数。这样,就别无选择了:需要访问非公有成员的非成员函数只能是类的友元函数。
本条款得出的结论如下。假设f是想正确声明的函数,c是和它相关的类:
·虚函数必须是成员函数。如果f必须是虚函数,就让它成为c的成员函数。
·operator>>和operator<<决不能是成员函数。如果f是operator>>或operator<<,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
·只有非成员函数对最左边的参数进行类型转换。如果f需要对最左边的参数进行类型转换,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
·其它情况下都声明为成员函数。如果以上情况都不是,让f成为c的成员函数。
#17
楼上的 angelo23(angelo) 和 ilovevc(linux_newbie) 的 “这个地方不声明为friend完全没有问题。”观点我不赞同。原因很简单,这各class并没有定义完全,这里仅仅是一个例子。
如果 成员操作符定义正这样的话,就必须要考虑到友元了:
Matrix& operator *(int n){ rows*=n;return *this;}
--- or ----
Matrix& operator *(int n){ rows*= n?n:1;cols *=n?n:1;return *this;}
如果 成员操作符定义正这样的话,就必须要考虑到友元了:
Matrix& operator *(int n){ rows*=n;return *this;}
--- or ----
Matrix& operator *(int n){ rows*= n?n:1;cols *=n?n:1;return *this;}
#18
1.这里的乘号操作符为什么要声明为友元,声明为成员函数不行么?声明为友元仅仅是为了使用两个参数?
a.声明成友元,是为了在全局函数operator*(int, mat)中可以调用mat的私有成员,在这里可以不声明友元,因为在operator*(int, mat)中没有使用mat的私有私有成员。
b. 声明为成员函数不行,除非你能改变int增加operator*(mat),但这是不行了,如果 int也是自定义的类型,就可以这样声明了。
2.这种友元调用不就是5.operator*(mat)么? 那么这种声明会不会重载了整型的乘号操作符?
这种友元调用不是5.operator*(mat),因为int没有成员函数operator*, 5 * mat调用的
只是全局函数operator*(int, mat)。如果整型的乘号调用的是全局函数operator*(int, int)(这一点我不能确定),那么可以说明对全局函数operator*的重载。
3.编译器能自动识别在这个友元中的这些参数的前后位置吗?把n 与mat的声明位置换一下可以么?
不能,编译器严格的按照前后次序进行编译,因此,把n与mat的声明位置换一下,意义完全不一样。
a.声明成友元,是为了在全局函数operator*(int, mat)中可以调用mat的私有成员,在这里可以不声明友元,因为在operator*(int, mat)中没有使用mat的私有私有成员。
b. 声明为成员函数不行,除非你能改变int增加operator*(mat),但这是不行了,如果 int也是自定义的类型,就可以这样声明了。
2.这种友元调用不就是5.operator*(mat)么? 那么这种声明会不会重载了整型的乘号操作符?
这种友元调用不是5.operator*(mat),因为int没有成员函数operator*, 5 * mat调用的
只是全局函数operator*(int, mat)。如果整型的乘号调用的是全局函数operator*(int, int)(这一点我不能确定),那么可以说明对全局函数operator*的重载。
3.编译器能自动识别在这个友元中的这些参数的前后位置吗?把n 与mat的声明位置换一下可以么?
不能,编译器严格的按照前后次序进行编译,因此,把n与mat的声明位置换一下,意义完全不一样。
#19
指出FreeFice(庄鱼) 的一个错误
即使如你所说
增加一个定义: Matrix& operator *(int n){ rows*=n;return *this;}
这种情况也不需要定义成friend. 我估计你是对friend的理解有误.
只要Matrix& operator *(int n)这个函数定义在public section中,外部的函数就可以使用,而不管这个函数里面的实现是不是用到了private的变量,例如rows等.
即使如你所说
增加一个定义: Matrix& operator *(int n){ rows*=n;return *this;}
这种情况也不需要定义成friend. 我估计你是对friend的理解有误.
只要Matrix& operator *(int n)这个函数定义在public section中,外部的函数就可以使用,而不管这个函数里面的实现是不是用到了private的变量,例如rows等.
#20
回: ilovevc(linux_newbie)
我明白你的意思了。这确实是疏忽了……
只有当Matrix& operator *(int n,Matrix& mat){mat.row*=n;return mat;};而,
Matrix& operator *(int n){mat.col*=n;return *this;}这种情况才必须要用到friend!
我明白你的意思了。这确实是疏忽了……
只有当Matrix& operator *(int n,Matrix& mat){mat.row*=n;return mat;};而,
Matrix& operator *(int n){mat.col*=n;return *this;}这种情况才必须要用到friend!
#21
楼主咋不见了?呵呵,出来说句话啊,这个例子哪里来的啊?
BTW,反正我是坚持认为这里声明为friend完全没有必要。
BTW,反正我是坚持认为这里声明为friend完全没有必要。
#22
看来很多人对friend Matrix & operator *(int n,Matrix &mat);这一句的问题所在不太清楚,这里我解释一下,请多指教:
1、是否用friend取决于是否需要在类之外访问类内的非public成员,贴主把所有成员声明为public,这是个很不好的习惯。
2、返回类型Matrix与Matrix&是有很大差异的,要返回Matrix&就要求返回的变量是存在的,即,不能是临时变量,需要类似FreeFice(庄鱼)说的Matrix& operator *(int n,Matrix& mat){mat.row*=n;return mat;};但是按FreeFice(庄鱼)这样,mat变量被改了,这是很怪异的事情,而且一般传入参数mat一般会声明为const,这是因为普通变量可以自动转换成const,而反之却不行。如果返回类型是Matrix就可以返回临时变量,通常情况下就需要自己先实现一个拷贝构造函数,因为缺省的拷贝构造函数不能自动分配内存,即前面我的例子中的data(要自己分配内存)。
3、结果:friend Matrix operator * (const int n, const Matrix & mat);
1、是否用friend取决于是否需要在类之外访问类内的非public成员,贴主把所有成员声明为public,这是个很不好的习惯。
2、返回类型Matrix与Matrix&是有很大差异的,要返回Matrix&就要求返回的变量是存在的,即,不能是临时变量,需要类似FreeFice(庄鱼)说的Matrix& operator *(int n,Matrix& mat){mat.row*=n;return mat;};但是按FreeFice(庄鱼)这样,mat变量被改了,这是很怪异的事情,而且一般传入参数mat一般会声明为const,这是因为普通变量可以自动转换成const,而反之却不行。如果返回类型是Matrix就可以返回临时变量,通常情况下就需要自己先实现一个拷贝构造函数,因为缺省的拷贝构造函数不能自动分配内存,即前面我的例子中的data(要自己分配内存)。
3、结果:friend Matrix operator * (const int n, const Matrix & mat);
#23
friend Matrix & operator *(int n,Matrix &mat);
我认为没有必要声明为有元。
1)类接口的目标是完整且最小。
2)因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
我认为没有必要声明为有元。
1)类接口的目标是完整且最小。
2)因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
#24
申明为友元可以话
可以使用5*mat
如果是成员函数的话
就不可以
只可以mat*5
可以使用5*mat
如果是成员函数的话
就不可以
只可以mat*5
#25
失踪了一天真不好意思,其实这个函数的确是没有必要声明为友元的。请大家不要怪我。这里介绍一下这段小程序的出处。这段小程序是来自北大青鸟第二学期ACCP3.0教材中的C++中的其中一章,好像是操作符重载的一节吧。本来我是不明白为什么这个函数有两个参数的,关于友元只是附带的,这个问题连教的老师也没讲得清楚。但没想到这个问题会扯出一大堆我没有预想到的问题,而且也长了很多知识。真的非常感谢大家的热烈讨论,也感谢CSDN提供也这个场所。
另:这个贴只有100,实在为难啊,要怎分配呢?大家都讲得很好,也很清晰。特别是巴巴罗萨,太感谢他了。
另:这个贴只有100,实在为难啊,要怎分配呢?大家都讲得很好,也很清晰。特别是巴巴罗萨,太感谢他了。
#26
回 gernal_dn() :
我在上面举的那个例子是有意识那样做的,你也许是觉得怪异,就像我当初学数学时一样,对·和X两种乘法可是花了好长时间呢
不用引用是可以的,但那已不是我的目的了
我在上面举的那个例子是有意识那样做的,你也许是觉得怪异,就像我当初学数学时一样,对·和X两种乘法可是花了好长时间呢
不用引用是可以的,但那已不是我的目的了
#27
只要把问题搞清楚就行了,近来看帖子又不是为分来的!要分的话,还不如灌水呢……
你要是再不结贴,大家会以为你还没落明白呢。
你要是再不结贴,大家会以为你还没落明白呢。
#1
1. 可以声明为成员函数。
2. 这种友无调用就是5.operaor*(mat),不会重载整型的乘号操作符。
3. 编译能自动识别在这个友元中的这些参数的前后位置。不可以。
2. 这种友无调用就是5.operaor*(mat),不会重载整型的乘号操作符。
3. 编译能自动识别在这个友元中的这些参数的前后位置。不可以。
#2
1.声明成友元使函数和类的成员函数具有相同的权限,能存取类的私有成员。
3.编译器不能自动识别,除非自己写它的相关的重栽形式。
3.编译器不能自动识别,除非自己写它的相关的重栽形式。
#3
重载运算符的时候,要求运算符的左边必须为类本身(如重载<<)如果要在右边用类的话就必须,申明成友元,友元的存取权限和本身曾员一样
#4
1。这里声明为友元函数的最大好处就是可以采用将内建型别作为*号的第一参数(左参)从而达到与内建型别运算规则完全一致的目的。i.e.当你不声明friend,而只是一个成员函数的话,就不能使用5*mat;
2。不是对整型*号的重载。C++不同于Java,没有类似Java的Integer这样的外包类,所以对于C++内建型别(注意:它不是类,没有对象)来说也不存在什么重载了。
3。编译器不能自动识别,如果按照你对*号两个参数的重载,其意义很明确:第一参表示乘号左边的操作数,第二参表示右边的操作数。
2。不是对整型*号的重载。C++不同于Java,没有类似Java的Integer这样的外包类,所以对于C++内建型别(注意:它不是类,没有对象)来说也不存在什么重载了。
3。编译器不能自动识别,如果按照你对*号两个参数的重载,其意义很明确:第一参表示乘号左边的操作数,第二参表示右边的操作数。
#5
用了友员就没有函数的参数的左右之分
3。编译器不能自动识别,如果按照你对*号两个参数的重载,其意义很明确:第一参表示乘号左边的操作数,第二参表示右边的操作数。???????????????(好象是不对~)
3。编译器不能自动识别,如果按照你对*号两个参数的重载,其意义很明确:第一参表示乘号左边的操作数,第二参表示右边的操作数。???????????????(好象是不对~)
#6
呵呵。好像大家的说法不太一致...
#7
to bshaozi(俺们那噶不学c!)
RookieStar(★)(巴巴罗萨) 是对的
对于你认为的那种不分左右的情况,实际上是由于类存在一个构造函数可以让编译器进行隐式转换
对于本例,如果Matrix有一个构造函数Matrix(int)的话,那么它定义一个友元
friend Matrix& operator*(const Matrix& lhs, const Matrix& rhs)就可以胜任以下情况:
Matrix * Matrix
Matrix * int
int * Matrix
RookieStar(★)(巴巴罗萨) 是对的
对于你认为的那种不分左右的情况,实际上是由于类存在一个构造函数可以让编译器进行隐式转换
对于本例,如果Matrix有一个构造函数Matrix(int)的话,那么它定义一个友元
friend Matrix& operator*(const Matrix& lhs, const Matrix& rhs)就可以胜任以下情况:
Matrix * Matrix
Matrix * int
int * Matrix
#8
呵呵,心宇兄说的好!
#9
guifuli(一览众山小) 回答得很清楚
有关友元参数对调做点补充说明:
根据你定义的情况,是不可以的,但是如果你的成员函数没有operator *(int)的话,是可以对掉的,这样一来,你就需要定义2个友元操作符了。
有关友元参数对调做点补充说明:
根据你定义的情况,是不可以的,但是如果你的成员函数没有operator *(int)的话,是可以对掉的,这样一来,你就需要定义2个友元操作符了。
#10
1. 操作符出现在非本类右边(后面),必须用有员,以使得它类可以使用本类的私有成员(当然,如果你所有的都是public,可以例外);
2. 正如你所说,确实重载了int 的*操作符,比如我们经常需要重载ostream类的操作符<<
3. 编译器可以识别。
4. friend Matrix & operator *(int n,Matrix &mat);这种方式是有些问题的,这样更好:
Matrix operator * (const int, const Matrix&); 并且需要实现Matrix类的拷贝构造函数:
Matrix(const Matrix&); 以及重载
Matrix & operator = (const Matrix&);
按惯例,实现这样的乘法之前应该先实现重载操作符*=
Matrix & operator *= (const int);
这是因为这样的乘法需要返回临时变量所致。
2. 正如你所说,确实重载了int 的*操作符,比如我们经常需要重载ostream类的操作符<<
3. 编译器可以识别。
4. friend Matrix & operator *(int n,Matrix &mat);这种方式是有些问题的,这样更好:
Matrix operator * (const int, const Matrix&); 并且需要实现Matrix类的拷贝构造函数:
Matrix(const Matrix&); 以及重载
Matrix & operator = (const Matrix&);
按惯例,实现这样的乘法之前应该先实现重载操作符*=
Matrix & operator *= (const int);
这是因为这样的乘法需要返回临时变量所致。
#11
可以参考一下:
class matrix
{
public:
matrix(const int, const int);
matrix(const matrix&);
~matrix(void);
double & operator()(const int, const int);
matrix & operator = (const matrix&);
matrix & operator = (const double *);
matrix & operator += (const matrix&);
matrix operator + (const matrix&);
matrix & operator -= (const matrix&);
matrix operator - (const matrix&);
matrix & operator *= (const double&);
matrix operator * (const double&);
friend matrix operator * (const double&, const matrix&);
friend ostream & operator << (ostream &, const matrix&);
int resize(const int, const int);
private:
int setmemsize(const int, const int);
int L_X, L_Y;
double *data;
};
int matrix::setmemsize(const int x, const int y)
{
L_X = x;
L_Y = y;
data = new double[x*y];
memset(data, 0, x * y * sizeof(double));
return 0;
}
matrix::matrix(const int x, const int y)
{
setmemsize(x,y);
}
matrix::matrix(const matrix& m)
{
setmemsize(m.L_X, m.L_Y);
memcpy(data, m.data, L_X * L_Y * sizeof(double));
}
matrix::~matrix(void)
{
delete[]data;
}
double & matrix::operator () (const int x, const int y)
{
return *(data + y *L_X + x);
}
ostream & operator << (ostream & os, const matrix& m)
{
double *d = m.data;
for (int y = 0; y < m.L_Y; ++y)
for (int x = 0; x < m.L_X; ++x,++d) os<<*d<<'\t';
return os;
}
int matrix::resize(const int x, const int y)
{
delete[]data;
return setmemsize(x,y);
}
matrix & matrix::operator = (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
memcpy(data, m.data, L_X * L_Y * sizeof(double));
return *this;
}
matrix & matrix::operator = (const double *d)
{
memcpy(data, d, L_X * L_Y * sizeof(double));
return *this;
}
matrix & matrix::operator += (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
int size = L_X * L_Y;
double* m1 = data;
double* m2 = m.data;
for(; size--; *(m1++)+=*(m2++));
return *this;
}
matrix matrix::operator + (const matrix& m)
{
matrix temp(L_X, L_Y);
if ((L_X != m.L_X) || (L_Y != m.L_Y)) return matrix(temp);
temp = *this;
temp += m;
return matrix(temp);
}
matrix & matrix::operator -= (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
int size = L_X * L_Y;
double* m1 = data;
double* m2 = m.data;
for(; size--; *(m1++)-=*(m2++));
return *this;
}
matrix matrix::operator - (const matrix& m)
{
matrix temp(L_X, L_Y);
if ((L_X != m.L_X) || (L_Y != m.L_Y)) return matrix(temp);
temp = *this;
temp -= m;
return matrix(temp);
}
matrix & matrix::operator *= (const double& d)
{
int size = L_X * L_Y;
double* m = data;
for(; size--; *(m++) -= d);
return *this;
}
matrix matrix::operator * (const double& d)
{
matrix temp(*this);
temp *= d;
return matrix(temp);
}
matrix operator * (const double& d, const matrix& m)
{
matrix temp(m);
temp *= d;
return matrix(temp);
}
class matrix
{
public:
matrix(const int, const int);
matrix(const matrix&);
~matrix(void);
double & operator()(const int, const int);
matrix & operator = (const matrix&);
matrix & operator = (const double *);
matrix & operator += (const matrix&);
matrix operator + (const matrix&);
matrix & operator -= (const matrix&);
matrix operator - (const matrix&);
matrix & operator *= (const double&);
matrix operator * (const double&);
friend matrix operator * (const double&, const matrix&);
friend ostream & operator << (ostream &, const matrix&);
int resize(const int, const int);
private:
int setmemsize(const int, const int);
int L_X, L_Y;
double *data;
};
int matrix::setmemsize(const int x, const int y)
{
L_X = x;
L_Y = y;
data = new double[x*y];
memset(data, 0, x * y * sizeof(double));
return 0;
}
matrix::matrix(const int x, const int y)
{
setmemsize(x,y);
}
matrix::matrix(const matrix& m)
{
setmemsize(m.L_X, m.L_Y);
memcpy(data, m.data, L_X * L_Y * sizeof(double));
}
matrix::~matrix(void)
{
delete[]data;
}
double & matrix::operator () (const int x, const int y)
{
return *(data + y *L_X + x);
}
ostream & operator << (ostream & os, const matrix& m)
{
double *d = m.data;
for (int y = 0; y < m.L_Y; ++y)
for (int x = 0; x < m.L_X; ++x,++d) os<<*d<<'\t';
return os;
}
int matrix::resize(const int x, const int y)
{
delete[]data;
return setmemsize(x,y);
}
matrix & matrix::operator = (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
memcpy(data, m.data, L_X * L_Y * sizeof(double));
return *this;
}
matrix & matrix::operator = (const double *d)
{
memcpy(data, d, L_X * L_Y * sizeof(double));
return *this;
}
matrix & matrix::operator += (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
int size = L_X * L_Y;
double* m1 = data;
double* m2 = m.data;
for(; size--; *(m1++)+=*(m2++));
return *this;
}
matrix matrix::operator + (const matrix& m)
{
matrix temp(L_X, L_Y);
if ((L_X != m.L_X) || (L_Y != m.L_Y)) return matrix(temp);
temp = *this;
temp += m;
return matrix(temp);
}
matrix & matrix::operator -= (const matrix& m)
{
if ((L_X != m.L_X) || (L_Y != m.L_Y)) resize(m.L_X, m.L_Y);
int size = L_X * L_Y;
double* m1 = data;
double* m2 = m.data;
for(; size--; *(m1++)-=*(m2++));
return *this;
}
matrix matrix::operator - (const matrix& m)
{
matrix temp(L_X, L_Y);
if ((L_X != m.L_X) || (L_Y != m.L_Y)) return matrix(temp);
temp = *this;
temp -= m;
return matrix(temp);
}
matrix & matrix::operator *= (const double& d)
{
int size = L_X * L_Y;
double* m = data;
for(; size--; *(m++) -= d);
return *this;
}
matrix matrix::operator * (const double& d)
{
matrix temp(*this);
temp *= d;
return matrix(temp);
}
matrix operator * (const double& d, const matrix& m)
{
matrix temp(m);
temp *= d;
return matrix(temp);
}
#12
我的看法:这个地方不声明为friend完全没有问题。声明为friend对于参数传递、匹配什么的,根本没有影响,只不过friend可以存取把它声明为friend的类的private成员了,亦即,完全是访问控制的问题。可是这里,在全局函数Matrix &operator *(int n,Matrix &mat)中,根本没有牵涉到Matrix类的private成员的访问,可以认为它只是一个辅助的函数(这样Matrix类的interface可以“减肥”一点,B.S.教导我们,类的interface不要过于fat)。不信的话大家可以试一试,在这里不把这个全局的*操作符重载函数声明为Matrix的friend,完全没有问题。
#13
基本上可以看出,写出这个class的人应该水平不错.
但是大部分的问答我感觉都有些错误.
1.这里的乘号操作符为什么要声明为友元,声明为成员函数不行么?声明为友元仅仅是为了使用两个参数?
对于你这种特定的情况来说,声明友元主要是一种习惯,因为现在你的实现并没有用到非public的东西,所以去掉也可以.angelo23(angelo)说的不错. 除了惯例以外,还有一个原因是,如果以后你修改了这个全局函数的定义,例如需要使用某些private的成员时,你不需要去修改Matrix类里面的东西了.
另外,声明成成员函数是不行的,它就无法实现 5 * matrix 这样的语句.
2.这种友元调用不就是5.operator*(mat)么? 那么这种声明会不会重载了整型的乘号操作符?
这种友元不是调用5.operator*(mat). int已经是内置的类型,并没有定义这样的操作(也不可能,你难道期望C++能够预见到某天有人会写出一个Matrix类吗?)因此也就没有重载int的*操作符. C++的编译器在遇到x * y的时候,它会查找定义在X类中的成员函数 X::operator*(Y y), 如果没有找到,那么就在全局范围内查找 operator *(X x, Y y)或者使用引用的版本. 结果就匹配上了刚刚定义的Matrix& operator *(int n,Matrix &mat)
3.编译器能自动识别在这个友元中的这些参数的前后位置吗?把n 与mat的声明位置换一下可以么?
不太明白你的第一个问题. 位置不能换.现在的Matrix 的定义已经比较完整了.
例如我们调用 5 * matrix, 那么就调用全局范围内的
Matrix& operator *(int n,Matrix &mat)
如果调用 matrix * 5, 那么就调用Matrix类里面定义的
Matrix& Matrix::opreator *(int x)
如果你调整了次序,将全局的那个改为Matrix& operator *(Matrix &mat, int n), 那么两个都是期望Matrix的类型在左边, 也就是仅仅支持 Matrix * 5, 而无法支持 5 * matrix 了.
另外,楼上某些说的构造函数隐式转换,在你的定义中看不出来,也和你问的问题关系不大.
但是大部分的问答我感觉都有些错误.
1.这里的乘号操作符为什么要声明为友元,声明为成员函数不行么?声明为友元仅仅是为了使用两个参数?
对于你这种特定的情况来说,声明友元主要是一种习惯,因为现在你的实现并没有用到非public的东西,所以去掉也可以.angelo23(angelo)说的不错. 除了惯例以外,还有一个原因是,如果以后你修改了这个全局函数的定义,例如需要使用某些private的成员时,你不需要去修改Matrix类里面的东西了.
另外,声明成成员函数是不行的,它就无法实现 5 * matrix 这样的语句.
2.这种友元调用不就是5.operator*(mat)么? 那么这种声明会不会重载了整型的乘号操作符?
这种友元不是调用5.operator*(mat). int已经是内置的类型,并没有定义这样的操作(也不可能,你难道期望C++能够预见到某天有人会写出一个Matrix类吗?)因此也就没有重载int的*操作符. C++的编译器在遇到x * y的时候,它会查找定义在X类中的成员函数 X::operator*(Y y), 如果没有找到,那么就在全局范围内查找 operator *(X x, Y y)或者使用引用的版本. 结果就匹配上了刚刚定义的Matrix& operator *(int n,Matrix &mat)
3.编译器能自动识别在这个友元中的这些参数的前后位置吗?把n 与mat的声明位置换一下可以么?
不太明白你的第一个问题. 位置不能换.现在的Matrix 的定义已经比较完整了.
例如我们调用 5 * matrix, 那么就调用全局范围内的
Matrix& operator *(int n,Matrix &mat)
如果调用 matrix * 5, 那么就调用Matrix类里面定义的
Matrix& Matrix::opreator *(int x)
如果你调整了次序,将全局的那个改为Matrix& operator *(Matrix &mat, int n), 那么两个都是期望Matrix的类型在左边, 也就是仅仅支持 Matrix * 5, 而无法支持 5 * matrix 了.
另外,楼上某些说的构造函数隐式转换,在你的定义中看不出来,也和你问的问题关系不大.
#14
thinking in c++的重载那一部分应该由相关描述
#15
好多高手,学习学习!
#16
看来这个帖子一定要有重量级人物登场才能结贴,以下载自Scott Meyers《Effective C++》算是对我的回答的补充以及大家讨论的总结 :P
条款19: 分清成员函数,非成员函数和友元函数
成员函数和非成员函数最大的区别在于成员函数可以是虚拟的而非成员函数不行。所以,如果有个函数必须进行动态绑定(见条款38),就要采用虚拟函数,而虚拟函数必定是某个类的成员函数。关于这一点就这么简单。如果函数不必是虚拟的,情况就稍微复杂一点。
看下面表示有理数的一个类:
class rational {
public:
rational(int numerator = 0, int denominator = 1);
int numerator() const;
int denominator() const;
private:
...
};
这是一个没有一点用处的类。(用条款18的术语来说,接口的确最小,但远不够完整。)所以,要对它增加加,减,乘等算术操作支持,但是,该用成员函数还是非成员函数,或者,非成员的友元函数来实现呢?
当拿不定主意的时候,用面向对象的方法来考虑!有理数的乘法是和rational类相联系的,所以,写一个成员函数把这个操作包到类中。
class rational {
public:
...
const rational operator*(const rational& rhs) const;
};
(如果你不明白为什么这个函数以这种方式声明——返回一个const值而取一个const的引用作为它的参数——参考条款21-23。)
现在可以很容易地对有理数进行乘法操作:
rational oneeighth(1, 8);
rational onehalf(1, 2);
rational result = onehalf * oneeighth; // 运行良好
result = result * oneeighth; // 运行良好
但不要满足,还要支持混合类型操作,比如,rational要能和int相乘。但当写下下面的代码时,只有一半工作:
result = onehalf * 2; // 运行良好
result = 2 * onehalf; // 出错!
这是一个不好的苗头。记得吗?乘法要满足交换律。
如果用下面的等价函数形式重写上面的两个例子,问题的原因就很明显了:
result = onehalf.operator*(2); // 运行良好
result = 2.operator*(onehalf); // 出错!
对象onehalf是一个包含operator*函数的类的实例,所以编译器调用了那个函数。而整数2没有相应的类,所以没有operator*成员函数。编译器还会去搜索一个可以象下面这样调用的非成员的operator*函数(即,在某个可见的名字空间里的operator*函数或全局的operator*函数):
result = operator*(2, onehalf); // 错误!
但没有这样一个参数为int和rational的非成员operator*函数,所以搜索失败。
再看看那个成功的调用。它的第二参数是整数2,然而rational::operator*期望的参数却是rational对象。怎么回事?为什么2在一个地方可以工作而另一个地方不行?
秘密在于隐式类型转换。编译器知道传的值是int而函数需要的是rational,但它也同时知道调用rational的构造函数将int转换成一个合适的rational,所以才有上面成功的调用(见条款m19)。换句话说,编译器处理这个调用时的情形类似下面这样:
const rational temp(2); // 从2产生一个临时
// rational对象
result = onehalf * temp; // 同onehalf.operator*(temp);
当然,只有所涉及的构造函数没有声明为explicit的情况下才会这样,因为explicit构造函数不能用于隐式转换,这正是explicit的含义。如果rational象下面这样定义:
class rational {
public:
explicit rational(int numerator = 0, // 此构造函数为
int denominator = 1); // explicit
...
const rational operator*(const rational& rhs) const;
...
};
那么,下面的语句都不能通过编译:
result = onehalf * 2; // 错误!
result = 2 * onehalf; // 错误!
这不会为混合运算提供支持,但至少两条语句的行为一致了。
然而,我们刚才研究的这个类是要设计成可以允许固定类型到rational的隐式转换的——这就是为什么rational的构造函数没有声明为explicit的原因。这样,编译器将执行必要的隐式转换使上面result的第一个赋值语句通过编译。实际上,如果需要的话,编译器会对每个函数的每个参数执行这种隐式类型转换。但它只对函数参数表中列出的参数进行转换,决不会对成员函数所在的对象(即,成员函数中的*this指针所对应的对象)进行转换。这就是为什么这个语句可以工作:
result = onehalf.operator*(2); // converts int -> rational
而这个语句不行:
result = 2.operator*(onehalf); // 不会转换
// int -> rational
第一种情形操作的是列在函数声明中的一个参数,而第二种情形不是。
尽管如此,你可能还是想支持混合型的算术操作,而实现的方法现在应该清楚了:使operator*成为一个非成员函数,从而允许编译器对所有的参数执行隐式类型转换:
class rational {
... // contains no operator*
};
// 在全局或某一名字空间声明,
// 参见条款m20了解为什么要这么做
const rational operator*(const rational& lhs,
const rational& rhs)
{
return rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
rational onefourth(1, 4);
rational result;
result = onefourth * 2; // 工作良好
result = 2 * onefourth; // 万岁, 它也工作了!
这当然是一个完美的结局,但还有一个担心:operator*应该成为rational类的友元吗?
这种情况下,答案是不必要。因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
然而,很多情况下,不是成员的函数从概念上说也可能是类接口的一部分,它们需要访问类的非公有成员的情况也不少。
让我们回头再来看看本书那个主要的例子,string类。如果想重载operator>>和operator<<来读写string对象,你会很快发现它们不能是成员函数。如果是成员函数的话,调用它们时就必须把string对象放在它们的左边:
// 一个不正确地将operator>>和
// operator<<作为成员函数的类
class string {
public:
string(const char *value);
...
istream& operator>>(istream& input);
ostream& operator<<(ostream& output);
private:
char *data;
};
string s;
s >> cin; // 合法, 但
// 有违常规
s << cout; // 同上
这会把别人弄糊涂。所以这些函数不能是成员函数。注意这种情况和前面的不同。这里的目标是自然的调用语法,前面关心的是隐式类型转换。
所以,如果来设计这些函数,就象这样:
istream& operator>>(istream& input, string& string)
{
delete [] string.data;
read from input into some memory, and make string.data
point to it
return input;
}
ostream& operator<<(ostream& output,
const string& string)
{
return output << string.data;
}
注意上面两个函数都要访问string类的data成员,而这个成员是私有(private)的。但我们已经知道,这个函数一定要是非成员函数。这样,就别无选择了:需要访问非公有成员的非成员函数只能是类的友元函数。
本条款得出的结论如下。假设f是想正确声明的函数,c是和它相关的类:
·虚函数必须是成员函数。如果f必须是虚函数,就让它成为c的成员函数。
·operator>>和operator<<决不能是成员函数。如果f是operator>>或operator<<,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
·只有非成员函数对最左边的参数进行类型转换。如果f需要对最左边的参数进行类型转换,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
·其它情况下都声明为成员函数。如果以上情况都不是,让f成为c的成员函数。
条款19: 分清成员函数,非成员函数和友元函数
成员函数和非成员函数最大的区别在于成员函数可以是虚拟的而非成员函数不行。所以,如果有个函数必须进行动态绑定(见条款38),就要采用虚拟函数,而虚拟函数必定是某个类的成员函数。关于这一点就这么简单。如果函数不必是虚拟的,情况就稍微复杂一点。
看下面表示有理数的一个类:
class rational {
public:
rational(int numerator = 0, int denominator = 1);
int numerator() const;
int denominator() const;
private:
...
};
这是一个没有一点用处的类。(用条款18的术语来说,接口的确最小,但远不够完整。)所以,要对它增加加,减,乘等算术操作支持,但是,该用成员函数还是非成员函数,或者,非成员的友元函数来实现呢?
当拿不定主意的时候,用面向对象的方法来考虑!有理数的乘法是和rational类相联系的,所以,写一个成员函数把这个操作包到类中。
class rational {
public:
...
const rational operator*(const rational& rhs) const;
};
(如果你不明白为什么这个函数以这种方式声明——返回一个const值而取一个const的引用作为它的参数——参考条款21-23。)
现在可以很容易地对有理数进行乘法操作:
rational oneeighth(1, 8);
rational onehalf(1, 2);
rational result = onehalf * oneeighth; // 运行良好
result = result * oneeighth; // 运行良好
但不要满足,还要支持混合类型操作,比如,rational要能和int相乘。但当写下下面的代码时,只有一半工作:
result = onehalf * 2; // 运行良好
result = 2 * onehalf; // 出错!
这是一个不好的苗头。记得吗?乘法要满足交换律。
如果用下面的等价函数形式重写上面的两个例子,问题的原因就很明显了:
result = onehalf.operator*(2); // 运行良好
result = 2.operator*(onehalf); // 出错!
对象onehalf是一个包含operator*函数的类的实例,所以编译器调用了那个函数。而整数2没有相应的类,所以没有operator*成员函数。编译器还会去搜索一个可以象下面这样调用的非成员的operator*函数(即,在某个可见的名字空间里的operator*函数或全局的operator*函数):
result = operator*(2, onehalf); // 错误!
但没有这样一个参数为int和rational的非成员operator*函数,所以搜索失败。
再看看那个成功的调用。它的第二参数是整数2,然而rational::operator*期望的参数却是rational对象。怎么回事?为什么2在一个地方可以工作而另一个地方不行?
秘密在于隐式类型转换。编译器知道传的值是int而函数需要的是rational,但它也同时知道调用rational的构造函数将int转换成一个合适的rational,所以才有上面成功的调用(见条款m19)。换句话说,编译器处理这个调用时的情形类似下面这样:
const rational temp(2); // 从2产生一个临时
// rational对象
result = onehalf * temp; // 同onehalf.operator*(temp);
当然,只有所涉及的构造函数没有声明为explicit的情况下才会这样,因为explicit构造函数不能用于隐式转换,这正是explicit的含义。如果rational象下面这样定义:
class rational {
public:
explicit rational(int numerator = 0, // 此构造函数为
int denominator = 1); // explicit
...
const rational operator*(const rational& rhs) const;
...
};
那么,下面的语句都不能通过编译:
result = onehalf * 2; // 错误!
result = 2 * onehalf; // 错误!
这不会为混合运算提供支持,但至少两条语句的行为一致了。
然而,我们刚才研究的这个类是要设计成可以允许固定类型到rational的隐式转换的——这就是为什么rational的构造函数没有声明为explicit的原因。这样,编译器将执行必要的隐式转换使上面result的第一个赋值语句通过编译。实际上,如果需要的话,编译器会对每个函数的每个参数执行这种隐式类型转换。但它只对函数参数表中列出的参数进行转换,决不会对成员函数所在的对象(即,成员函数中的*this指针所对应的对象)进行转换。这就是为什么这个语句可以工作:
result = onehalf.operator*(2); // converts int -> rational
而这个语句不行:
result = 2.operator*(onehalf); // 不会转换
// int -> rational
第一种情形操作的是列在函数声明中的一个参数,而第二种情形不是。
尽管如此,你可能还是想支持混合型的算术操作,而实现的方法现在应该清楚了:使operator*成为一个非成员函数,从而允许编译器对所有的参数执行隐式类型转换:
class rational {
... // contains no operator*
};
// 在全局或某一名字空间声明,
// 参见条款m20了解为什么要这么做
const rational operator*(const rational& lhs,
const rational& rhs)
{
return rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
rational onefourth(1, 4);
rational result;
result = onefourth * 2; // 工作良好
result = 2 * onefourth; // 万岁, 它也工作了!
这当然是一个完美的结局,但还有一个担心:operator*应该成为rational类的友元吗?
这种情况下,答案是不必要。因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
然而,很多情况下,不是成员的函数从概念上说也可能是类接口的一部分,它们需要访问类的非公有成员的情况也不少。
让我们回头再来看看本书那个主要的例子,string类。如果想重载operator>>和operator<<来读写string对象,你会很快发现它们不能是成员函数。如果是成员函数的话,调用它们时就必须把string对象放在它们的左边:
// 一个不正确地将operator>>和
// operator<<作为成员函数的类
class string {
public:
string(const char *value);
...
istream& operator>>(istream& input);
ostream& operator<<(ostream& output);
private:
char *data;
};
string s;
s >> cin; // 合法, 但
// 有违常规
s << cout; // 同上
这会把别人弄糊涂。所以这些函数不能是成员函数。注意这种情况和前面的不同。这里的目标是自然的调用语法,前面关心的是隐式类型转换。
所以,如果来设计这些函数,就象这样:
istream& operator>>(istream& input, string& string)
{
delete [] string.data;
read from input into some memory, and make string.data
point to it
return input;
}
ostream& operator<<(ostream& output,
const string& string)
{
return output << string.data;
}
注意上面两个函数都要访问string类的data成员,而这个成员是私有(private)的。但我们已经知道,这个函数一定要是非成员函数。这样,就别无选择了:需要访问非公有成员的非成员函数只能是类的友元函数。
本条款得出的结论如下。假设f是想正确声明的函数,c是和它相关的类:
·虚函数必须是成员函数。如果f必须是虚函数,就让它成为c的成员函数。
·operator>>和operator<<决不能是成员函数。如果f是operator>>或operator<<,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
·只有非成员函数对最左边的参数进行类型转换。如果f需要对最左边的参数进行类型转换,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
·其它情况下都声明为成员函数。如果以上情况都不是,让f成为c的成员函数。
#17
楼上的 angelo23(angelo) 和 ilovevc(linux_newbie) 的 “这个地方不声明为friend完全没有问题。”观点我不赞同。原因很简单,这各class并没有定义完全,这里仅仅是一个例子。
如果 成员操作符定义正这样的话,就必须要考虑到友元了:
Matrix& operator *(int n){ rows*=n;return *this;}
--- or ----
Matrix& operator *(int n){ rows*= n?n:1;cols *=n?n:1;return *this;}
如果 成员操作符定义正这样的话,就必须要考虑到友元了:
Matrix& operator *(int n){ rows*=n;return *this;}
--- or ----
Matrix& operator *(int n){ rows*= n?n:1;cols *=n?n:1;return *this;}
#18
1.这里的乘号操作符为什么要声明为友元,声明为成员函数不行么?声明为友元仅仅是为了使用两个参数?
a.声明成友元,是为了在全局函数operator*(int, mat)中可以调用mat的私有成员,在这里可以不声明友元,因为在operator*(int, mat)中没有使用mat的私有私有成员。
b. 声明为成员函数不行,除非你能改变int增加operator*(mat),但这是不行了,如果 int也是自定义的类型,就可以这样声明了。
2.这种友元调用不就是5.operator*(mat)么? 那么这种声明会不会重载了整型的乘号操作符?
这种友元调用不是5.operator*(mat),因为int没有成员函数operator*, 5 * mat调用的
只是全局函数operator*(int, mat)。如果整型的乘号调用的是全局函数operator*(int, int)(这一点我不能确定),那么可以说明对全局函数operator*的重载。
3.编译器能自动识别在这个友元中的这些参数的前后位置吗?把n 与mat的声明位置换一下可以么?
不能,编译器严格的按照前后次序进行编译,因此,把n与mat的声明位置换一下,意义完全不一样。
a.声明成友元,是为了在全局函数operator*(int, mat)中可以调用mat的私有成员,在这里可以不声明友元,因为在operator*(int, mat)中没有使用mat的私有私有成员。
b. 声明为成员函数不行,除非你能改变int增加operator*(mat),但这是不行了,如果 int也是自定义的类型,就可以这样声明了。
2.这种友元调用不就是5.operator*(mat)么? 那么这种声明会不会重载了整型的乘号操作符?
这种友元调用不是5.operator*(mat),因为int没有成员函数operator*, 5 * mat调用的
只是全局函数operator*(int, mat)。如果整型的乘号调用的是全局函数operator*(int, int)(这一点我不能确定),那么可以说明对全局函数operator*的重载。
3.编译器能自动识别在这个友元中的这些参数的前后位置吗?把n 与mat的声明位置换一下可以么?
不能,编译器严格的按照前后次序进行编译,因此,把n与mat的声明位置换一下,意义完全不一样。
#19
指出FreeFice(庄鱼) 的一个错误
即使如你所说
增加一个定义: Matrix& operator *(int n){ rows*=n;return *this;}
这种情况也不需要定义成friend. 我估计你是对friend的理解有误.
只要Matrix& operator *(int n)这个函数定义在public section中,外部的函数就可以使用,而不管这个函数里面的实现是不是用到了private的变量,例如rows等.
即使如你所说
增加一个定义: Matrix& operator *(int n){ rows*=n;return *this;}
这种情况也不需要定义成friend. 我估计你是对friend的理解有误.
只要Matrix& operator *(int n)这个函数定义在public section中,外部的函数就可以使用,而不管这个函数里面的实现是不是用到了private的变量,例如rows等.
#20
回: ilovevc(linux_newbie)
我明白你的意思了。这确实是疏忽了……
只有当Matrix& operator *(int n,Matrix& mat){mat.row*=n;return mat;};而,
Matrix& operator *(int n){mat.col*=n;return *this;}这种情况才必须要用到friend!
我明白你的意思了。这确实是疏忽了……
只有当Matrix& operator *(int n,Matrix& mat){mat.row*=n;return mat;};而,
Matrix& operator *(int n){mat.col*=n;return *this;}这种情况才必须要用到friend!
#21
楼主咋不见了?呵呵,出来说句话啊,这个例子哪里来的啊?
BTW,反正我是坚持认为这里声明为friend完全没有必要。
BTW,反正我是坚持认为这里声明为friend完全没有必要。
#22
看来很多人对friend Matrix & operator *(int n,Matrix &mat);这一句的问题所在不太清楚,这里我解释一下,请多指教:
1、是否用friend取决于是否需要在类之外访问类内的非public成员,贴主把所有成员声明为public,这是个很不好的习惯。
2、返回类型Matrix与Matrix&是有很大差异的,要返回Matrix&就要求返回的变量是存在的,即,不能是临时变量,需要类似FreeFice(庄鱼)说的Matrix& operator *(int n,Matrix& mat){mat.row*=n;return mat;};但是按FreeFice(庄鱼)这样,mat变量被改了,这是很怪异的事情,而且一般传入参数mat一般会声明为const,这是因为普通变量可以自动转换成const,而反之却不行。如果返回类型是Matrix就可以返回临时变量,通常情况下就需要自己先实现一个拷贝构造函数,因为缺省的拷贝构造函数不能自动分配内存,即前面我的例子中的data(要自己分配内存)。
3、结果:friend Matrix operator * (const int n, const Matrix & mat);
1、是否用friend取决于是否需要在类之外访问类内的非public成员,贴主把所有成员声明为public,这是个很不好的习惯。
2、返回类型Matrix与Matrix&是有很大差异的,要返回Matrix&就要求返回的变量是存在的,即,不能是临时变量,需要类似FreeFice(庄鱼)说的Matrix& operator *(int n,Matrix& mat){mat.row*=n;return mat;};但是按FreeFice(庄鱼)这样,mat变量被改了,这是很怪异的事情,而且一般传入参数mat一般会声明为const,这是因为普通变量可以自动转换成const,而反之却不行。如果返回类型是Matrix就可以返回临时变量,通常情况下就需要自己先实现一个拷贝构造函数,因为缺省的拷贝构造函数不能自动分配内存,即前面我的例子中的data(要自己分配内存)。
3、结果:friend Matrix operator * (const int n, const Matrix & mat);
#23
friend Matrix & operator *(int n,Matrix &mat);
我认为没有必要声明为有元。
1)类接口的目标是完整且最小。
2)因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
我认为没有必要声明为有元。
1)类接口的目标是完整且最小。
2)因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
#24
申明为友元可以话
可以使用5*mat
如果是成员函数的话
就不可以
只可以mat*5
可以使用5*mat
如果是成员函数的话
就不可以
只可以mat*5
#25
失踪了一天真不好意思,其实这个函数的确是没有必要声明为友元的。请大家不要怪我。这里介绍一下这段小程序的出处。这段小程序是来自北大青鸟第二学期ACCP3.0教材中的C++中的其中一章,好像是操作符重载的一节吧。本来我是不明白为什么这个函数有两个参数的,关于友元只是附带的,这个问题连教的老师也没讲得清楚。但没想到这个问题会扯出一大堆我没有预想到的问题,而且也长了很多知识。真的非常感谢大家的热烈讨论,也感谢CSDN提供也这个场所。
另:这个贴只有100,实在为难啊,要怎分配呢?大家都讲得很好,也很清晰。特别是巴巴罗萨,太感谢他了。
另:这个贴只有100,实在为难啊,要怎分配呢?大家都讲得很好,也很清晰。特别是巴巴罗萨,太感谢他了。
#26
回 gernal_dn() :
我在上面举的那个例子是有意识那样做的,你也许是觉得怪异,就像我当初学数学时一样,对·和X两种乘法可是花了好长时间呢
不用引用是可以的,但那已不是我的目的了
我在上面举的那个例子是有意识那样做的,你也许是觉得怪异,就像我当初学数学时一样,对·和X两种乘法可是花了好长时间呢
不用引用是可以的,但那已不是我的目的了
#27
只要把问题搞清楚就行了,近来看帖子又不是为分来的!要分的话,还不如灌水呢……
你要是再不结贴,大家会以为你还没落明白呢。
你要是再不结贴,大家会以为你还没落明白呢。