c/c++语言开发共享C++笔记一(创建一个不带指针成员变量的类)[注意事项]

0. 参考代码 #ifndef __complex__ #define __complex__ #include <cmath> //前置声明 class ostre


0. 参考代码

  #ifndef __complex__  #define __complex__    #include <cmath>    //前置声明  class ostream;  class complex       //class head  {                   //{} class body  public:      complex(double r = 0, double i = 0)          :re(r), im(i)      {}      complex& operator += (const complex&);      double real() const { return re; }      double imag() const { return im; }        friend complex & __doapl(complex*, const complex&);        int func(const complex &param)      {          return param.re + param.im;      }  private:      double re, im;  };      inline complex&  __doapl(complex*ths, const complex&r)  {      ths->re += r.re;      ths->im += r.im;      return *ths;  }    inline complex&  complex::operator +=(const complex &r)  {      return __doapl(this, r);  }    #endif // !__complex__

1. 在头文件”complex.h”中 添加防御式声明

  #ifndef __complex__  #define __complex__  ...  #endif  //__complex__

这是为了防止头文件被多重包含(multiple inclusion),引发的编译问题

2. inline函数

函数直接在class声明内定义完成,会自动声明为inline函数,例如real(),image()就是inline函数或者加关键字声明 但是否是按照inline方式编译,是编译器决定的,我们只是建议编译器这做。

  inline double   imag(const complex& x)  {          return x.imag();    }

3. constructor(ctor,构造函数)

  complex(double r = 0; double i = 0)          :re(r), im(i)   //initialization list(初始化列表)      {}

如果不显示指定class的constructor,c++会默认创建构造函数,是没有参数的 构造函数没有返回值,可重载(overloading)

本例中,构造是显示指定的,并且有带有默认值的两个参数

initialization list(初始化列表):构造函数独有的,建议把成员变量放到这里初始化 成员变量放在初始化列表中和在函数体内部的区别是 初始化列表是在成员变量初始化的时候就已经改好了 函数体内部是赋值,是成员变量先初始化,然后再把赋值给成员变量,这样就多了一个步骤

注意构造函数的访问权限,大多数情况下,都是public的,但是在设计模式中的单例模式下,通常把构造函数设为private

4. [划重点]const member functions (常量成员函数)

  double real() const {return re;}

在类成员函数名称后面加上const,限定函数体内部不可改变本类的成员变量,注意,常量成员函数内部不能调用非常量成员函数

      double real()  { return re; }      double imag()  { return im; }

const类型的对象实例只能调用const声明过的成员函数

如果获取成员变量的函数不添加const,那按照下列使用是就会出错,

报错是因为 使用const创建 对象实例 c1,那就意味着c1所有的成员变量都是不可改变的,但是real(),image()并没有声明称cosnt常量成员函数,这就说明,在函数体内部是允许改变成员变量的, 存在歧义,故而报错

但是此种用法是合理的,报错就是因为我们设计的不合理,所以,强烈建议把不需要改变成员变量的成员函数使用const修饰

  // error  const complex c1(1, 2);    std::cout << c1.real() << std::endl;  std::cout << c1.imag() << std::endl;    /*  vs2013的报错  error c2662: “double complex::real(void)”: 不能将“this”指针从“const complex”转换为“complex &”      error c2662: “double complex::imag(void)”: 不能将“this”指针从“const complex”转换为“complex &”      */

5. 参数传递(值和引用)

(pass by value[值传递] vs pass by reference[引用传递](to const))

在函数参数传递中,使用引用传递,能够有效的提高传递过程中堆栈利用率,所以建议c++中进行函数参数传递的时候尽量使用引用,能够有效地节约堆栈空间

但是为了避免传入参数的值被意外修改,可以使用const complex &,const类型的引用

  complex & __doapl(complex*, const complex&);

6. friend(友元)

声明友元的函数可以直接访问的类的私有成员变量 相同class的各个object互为friends(友元)

  class complex  {   ...      friend complex & __doapl(complex*, const complex&);      int func(const complex &param)      {   return param.re + param.im;    }      ...  };  inline complex&  __doapl(complex*ths, const complex&r)  {      ths->re += r.re;      ths->im += r.im;      return *ths;  }    //相同class的各个object互为friends(友元)  complex c1(2,3);  complex c2;    c2.func(c1);

7. 操作符重载

1、重载操作符没必要一定是成员函数,还可以是友元函数。

2、重载操作符函数为成员函数主要是你需要操作类内部的成员, 必须是成员函数或友元函数才行。

3、至于由深浅拷贝的原因要使其成为成员函数,这个不知道。

4、如果运算符被重载为全局函数,那么只有一个参数的运算符叫做一元运算符,有两个参数的运算符叫做二元运算符。

如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数,因为对象自己成了左侧参数(cosnt t *this)。

语法上讲,运算符既可以定义为全局函数,也可以定义为成员函数。文献[murray , p44-p47]对此问题作了较多的阐述,并总结了表8-4-1的规则。

运算符 规则
所有的一元运算符 建议重载为成员函数
= () [] -> 只能重载为成员函数
+= -= /= *= &= = ~= %= >>= <<=
所有其它运算符 建议重载为全局函数

对于赋值操作符(=),这个比较特别,因为任何类如果不提供显示的拷贝赋值(即重载=),则编译器会隐式地提供一个。这样的话,如果你再通过友元声明,进行全局的定义会造成调用二义性(即使允许,编译也会出错)。

对于操作符(=,[],(),->),只能声明为成员函数是为了避免不合法的书写通过编译(这是推测出的原因,更深层的可能要研究 c++ 的设计了)。这涉及到 c++ 中类型的隐式转换。下面通过代码例子说明:

  #include <iostream>  class x   {  public:      x(){}      x(int){} // int 类型可以被隐式转换成 x      friend bool operator<(const x& x1, const x& x2) { return true; } // 只是测试,无意义  };    class y   {  public:      y(){}      y(int){} // int 类型可以被隐式转换成 y      bool operator<(const y& y) const { return true; } // 只是测试,无意义  };  int main()  {      x x;      if(1 < x) // 合法,使用友元重载函数,1 可以被隐式地转换为 x 类型 --友元函数的第一个参数      {}        y y;      if(1 < y) // 不合法,使用成员重载函数,函数的第一个参数是 const *this,1 不能被隐式转换      {}      return 0;    }    // 注:编译的时候可以通过注释掉不同的代码来查看错误(即合法性),后面解释不能作为友元全局重载的原因

由上面的代码可以知道,如果将 =,[],(),-> 进行友元全局重载,那么就会出现 1=x; 1[x]; 1->x; 1(x); 这样的合法语句(起码编译器认为这些是合法的)--参考代码中的 if(1

  #include <iostream>    class x   {  public:      x(){}      x(int){} // int 类型可以被隐式转换成 x      friend const x& operator+=(const x& x1, const x& x2) { return x1; } // 只是测试,无意义  };    int main()  {      x x;      1 += x;// 合法,使用友元重载函数,1 可以被隐式地转换为 x 类型 --友元函数的第一个参数      return 0;  }

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/606462.html

(0)
上一篇 2021年5月13日
下一篇 2021年5月13日

精彩推荐