拷贝构造函数中是否可以调用重载后的赋值运算操作符

今天有朋友在成都程序员QQ群里提出一个的问题:在一个类的拷贝构造函数里,是否可以调用重载后的赋值运算操作符,以便用一套代码来方便地实现对类成员的赋值处理。

00001: class Foo {
00002: public:
00003: Foo(const Foo & sour) {
00004: *this = sour;
00005: }
00006: Foo & operator = (const Foo & rv) {
00007: // do something
00008: }
00009: };

拷贝构造和赋值运算在语义上是完全不同的两件事,前者是“基于一个已存在的对象实例构造一个新的对象实例”,后者是“基于一个已存在的对象实例来更新另一个已存在的对象实例”。但是,除了语义上的不同,在语法上是否有问题呢?在逻辑上有没有问题呢?

语法肯定没有问题,上述代码可以顺利通过编译。

从逻辑上讲,似乎也看不出有什么问题。

进一步再想,在什么情况下必须自己来实现拷贝构造和赋值运算符呢?通常,是因为类成员中有指针,指向一些堆上的资源,不希望在复制和赋值的时候,出现两份指针指向同一份资源,比如:

00001: class Foo {
00002: char * data_;
00003: public:
00004: Foo() : data_(0) {}
00005: Foo(const Foo & sour) : data_(0) {
00006: *this = sour;
00007: }
00008: Foo & operator = (const Foo & rv) {
00009: if (&rv!=this) {
00010: if (0==data_)
00011: data_ = new char[DATA_SIZE];
00012: memcpy(data_, rv.data_, DATA_SIZE);
00013: }
00014: return *this;
00015: }
00016: ~Foo() {
00017: delete [] data_;
00018: }
00019: };

上面的代码的确是啰嗦了,如果拷贝构造函数写成下面这样,明显要清爽一些:

00001:     Foo(const Foo & sour)
00002: : data_(new char[DATA_SIZE])
00003: {
00004: memcpy(data_, sour.data_, DATA_SIZE);
00005: }

除了代码啰嗦,似乎也找不出更多的理由来反对这种做法(在拷贝构造中调用重载后的赋值运算符),但是从情理上似乎说不通,但要证明这种做法的错误,就必须举出一个很明显的反例,该反例能说明在某种情况下,这样调用必然会出问题。

可惜,我还没有想出一个例子,能对这种违反语义、但不违反语法,同时能够达到目的的做法进行 证伪。 
### 重载四则运算符作为类的成员函数 C++ 允许将四则运算符(如 `+`、`-`、`*`、`/`)重载为类的成员函数。这种实现方式的关键在于,运算符的左操作数是调用运算符的对象本身,而右操作数则作为函数参数传入。成员函数形式的运算重载通常用于操作两个相同类型的对象,或者当左操作数是当前类的实例时[^2]。 #### 成员函数实现的语法结构 在类中声明运算重载函数时,其定义通常如下: ```cpp class MyClass { private: int value; public: MyClass(int v) : value(v) {} // 重载加法运算符 MyClass operator+(const MyClass& other) const; int getValue() const { return value; } }; // 实现加法运算重载 MyClass MyClass::operator+(const MyClass& other) const { return MyClass(this->value + other.value); } ``` 上述代码展示了如何将加法运算重载为 `MyClass` 的成员函数。函数接受一个 `const MyClass&` 类型的参数,返回一个新的 `MyClass` 实例,其值为两个操作数的和。函数被定义为 `const`,以确保不会修改调用它的对象的状态。 #### 使用成员函数重载四则运算符的优势 - **封装性**:由于运算重载函数是类的成员函数,可以直接访问类的私有成员,无需额外的友元声明或公有访问函数。 - **简洁性**:成员函数的形式更加直观,因为左操作数隐式地作为调用对象存在,减少了参数的数量。 - **一致性**:对于某些操作(如赋值运算符 `+=`、`-=` 等),成员函数形式是唯一合法的实现方式,因为这些运算符需要修改对象的状态[^1]。 #### 成员函数与非成员函数的区别 虽然四则运算符可以通过成员函数实现重载,但在某些情况下,非成员函数可能更合适。例如,当运算符的左操作数不是当前类的实例时,必须使用非成员函数。此外,非成员函数支持隐式类型转换,而成员函数仅允许右操作数进行隐式转换。 例如,若希望支持 `MyClass obj + 10` 或 `10 + obj` 这样的表达式,其中 `obj` 是 `MyClass` 类型的对象,成员函数形式仅支持前者,而后者必须通过非成员函数实现: ```cpp // 非成员函数形式支持 10 + obj MyClass operator+(int lhs, const MyClass& rhs) { return MyClass(lhs + rhs.getValue()); } ``` #### 示例:重载减法运算符 以下示例展示了如何通过成员函数重载减法运算符 `operator-`: ```cpp class MyClass { private: int value; public: MyClass(int v) : value(v) {} // 重载减法运算符 MyClass operator-(const MyClass& other) const; int getValue() const { return value; } }; // 实现减法运算重载 MyClass MyClass::operator-(const MyClass& other) const { return MyClass(this->value - other.value); } ``` 使用时: ```cpp MyClass obj1(20); MyClass obj2(10); MyClass result = obj1 - obj2; // 调用重载的减法运算符 std::cout << result.getValue() << std::endl; // 输出: 10 ``` #### 注意事项 - **常量性**:运算重载函数应尽可能定义为 `const` 成员函数,以确保不会修改调用对象的状态。 - **返回类型**:返回类型应为新创建的对象,除非运算符涉及修改当前对象(如 `operator+=`)。 - **参数传递**:参数通常应以 `const` 引用方式传递,避免不必要的拷贝。 - **对称性**:若希望支持对称的运算符(如 `a + b` 和 `b + a`),需结合成员函数与非成员函数实现,以避免二义性问题[^4]。 --- ### 相关问题 1. 如何在 C++ 中通过非成员函数重载四则运算符? 2. 成员函数与非成员函数在重载四则运算符时有哪些适用场景? 3. 为什么某些运算符(如赋值运算符)只能通过成员函数实现? 4. 在 C++ 中,如何处理四则运算重载时的类型转换问题? 5. 四则运算重载是否支持链式操作?如何实现?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值