本篇笔记以复数类(不含指针的类)为例进行面向对象的学习

=========================================================

复数类的声明:

 1
class complex

 2
{

 3
public:

 4   complex (double r = 0, double i = 0): re (r), im (i) { }

 5   complex& operator += (const complex&);

 6   complex& operator -= (const complex&);

 7   complex& operator *= (const complex&);

 8   complex& operator /= (const complex&);

 9
double real () const { return re; }

10
double imag () const { return im; }

11
private:

12
double re, im;

13

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

15   friend complex& __doami (complex *, const complex&);

16   friend complex& __doaml (complex *, const complex&);

17 };

=========================================================

 一、构造函数

  1)C++列表初始化

    complex(double r = 0,double i = 0)

    :re(r),im(i)

    {}

  • 一般情况下,构造函数写在public区域.  一个例外情况是Singleton设计模式.
  • 构造函数没有返回值.
  • 只有构造函数可以使用 : () 构成的列表初始化语法. 
  • C++的初始化工作可以分为创建阶段和操作阶段.创建阶段是在列表初始化结束之后就结束了;在大括号中的所有操作都是对象已经被创建之后再去进行的操作.理论上讲,可以栽{}使用assign操作代替列表初始化.然而,这样做相当于是放弃了创建阶段的操作.此外,如果对象中含有const变量,是不能在创建阶段结束后再去修改的,这种情况下必须使用列表初始化

  2) 构造函数的调用方法

    构造函数不显式调用,有以下三种方法。

  • complex C1(2,3);
  • complex C2;//在构造函数定义中,实部和虚部均有默认值0;
  • complex* C3 = new complex(4);//这里并不是进行了一次显式调用,而是声明了一个匿名对象;声明匿名对象的方式就是采用之前的两种方法,区别在于其不存在变量名。如果不是通过new创建的堆变量,栈中的匿名对象的生命周期仅为当前行。 

  3) 构造函数的重载

    在C语言中不允许出现同名函数。但是在C++中,允许函数同名,但必须保证根据参数的不同可以找到唯一的一个函数实体与之对应,不能产生二义性。与大部分成员函数一样,构造函数可以被重载。

    * 在编译时,对于重载函数,编译器会给予其一个real name,例如complex::complex@doubledouble,其中包含的参数的类型信息。

     也就是说,重载后的函数实际上名字是不同的。

    * 当构造函数具有默认值是,应特别注意是否会产生二义性。

  4) 默认构造函数

    当没有定义构造函数时,C++会提供一个默认构造函数。默认构造函数不接受任何参数,不进行任何操作。

    只要用户定义了一个构造函数,不论其参数为何,C++将不再提供默认的构造函数。

  5) 构造函数在private区时的情况(Singleton设计模式)

    
 

 1
class A{

 2
public:

 3
static A& getinstance();

 4
setup() {...};

 5
private:

 6
A();

 7     A(const A & rhs);

 8
...

 9
};

10

11 A& A::getinstance()

12
{

13
static A a;//静态变量

14
return a;

15 }

  调用方法:

    A::getinstance().setup();

    //这种设计模式下,永远只有一个实例。

 
 

二、const 关键字

  double real () const { return re; }

  在参数表后的const,表示当前函数不会改变对象内部成员变量的值。  

  complex& operator += (const complex&);

  在参数前的const,表示当前参数对于成员函数是一个常量变量,且不会被改变。

  * 返回值也可以被指定为const类型。

  
 

  不加const会产生的后果:

  e.g.

    const complex C1(2,1);

    cout<<C1.real();

    如果在real函数定义时没有加上const关键字,C1.real()就不能执行,因为只看这一接口无法保证C1的数据成员不会被real函数修改。

 
 

三、 传值

  三种传值方案:值传递、引用传递、const引用传递

  1)参数传递

  • 尽量不使用值传递的传递方案,因为这样复制的效率太低了。引用是C++中提供的用来代替指针的一种方案。相比于指针,其好处是使用更简单,用户可以像值传递时一样使用它,而不用考虑其背后的实现方法;而使用指针时,用户必须知道这是一个指针,又涉及了&操作和*操作,不易于使用并且很容易误操作(指针使用不当产生的内存泄漏、溢出等问题)。
  • const 引用传递的目的在上一小节中已经解释过,就是为了解决常量对象的调用问题。
  • 在传参时,建议不论什么参数都采用引用传递的方式。

  2)返回值传递

  • 仍然推荐采用引用传递,但不再是不论什么情况都采用引用传递。
  • 采用引用传递的另一个好处是返回值可以作为左值继续进行操作。如a+b+c。
  • 不能返回引用的情况:
  1.  
    1. 变量为栈空间内的局部变量。
    2. 要返回的对象为一const对象。这种情况下,引用可能会带来对象内容的更改,所以只能采用值传递或const引用传递

  Appendix——关于引用:

  1)单纯引用的语法

    &ref = obj;

    ref成为obj的别名。作为参数传递时,就可以按照这种方式理解。(实际上并不是这样的操作,传参相当于构造,是在第一阶段完成的,而赋值是在第二阶段。不过如第一小节所述效果是一样的)

  2)作为返回值时。

    e.g. int & getnum(&i) {return i;}

    若使用int a=getnum(i):则相当于先调用了getnum(i),其返回就是i的实体;然后再用i进行了复制构造操作。也就是说,a和i是两个变量。

    若使用int&a = getnum(i):则相当于a也是i的别名。然而这要求用户知道返回方式为引用传递,因为引用并不能指向一个值。

    若getnum(i) = 1;则是对i进行了赋值操作。getnum(i)就是i的一个别名。(相当于是一个匿名变量。)

    事实上,返回的就是对象i;至于是引用传递还是值传递,则是接收方式的不同。

  注:引用不能无初始化存在,也不能被修改,可以视为常指针。

 
 

四、封装的后门——友元

  如果一个函数是类的友元函数,则该函数可以直接使用类的private成员,就像是类的成员函数一样。

  友元的两个存在方式:

  1. friend关键字在类的声明中声明。
  2. 同一个class的各个object互为友元。

    这就是为什么在运算符重载时,可以直接使用C2.real变量。也就是说,类的成员函数中的参数如果是同类对象,可以直接使用它的private对象。

 
 

五、特殊的函数重载——运算符重载

  运算符就可以视作一个函数。

  1) 作为成员函数重载(一般为单目)

  以+=运算符为例:

    
 

 1 inline complex&

 2 __doapl (complex* ths, const complex& r)

 3 {

 4   ths->re += r.re;

 5   ths->im += r.im;

 6   return *ths;

 7 }

 8 inline complex&

 9 complex::operator += (const complex& r)

10 {

11   return __doapl (this, r);

12 }

  • 编译器如何看待操作数?

    C2+=C1;《=》C2.operator +=(this , const complex &r);//其中r为C1的别名;

    * 任何的成员函数都有隐藏的this指针,这个指针只在C++的后台中才会体现,我们使用C++时不可见。

  • 返回引用

    考虑到存在连续操作的可能,应该为原类型的引用。

  • 运算符的结合方式:

    在连续操作时,涉及到运算符的结合方式。

    左结合,如:<<,[],+,-等;a+b+c <=> (a+b)+c

    右结合,如:=,+=,-+等;a=b=c <=> a=(b=c)

 
 

  2)作为非成员函数:

     以+为例的双目函数:

1
inline complex

2
operator + (double x, const complex& y)

3
{

4
return complex (x + real (y), imag (y));//返回一个匿名临时变量

5 }

    * 返回值一般采取值传递

    e.g.

      对于a=b+c,若b+c采取了引用传递,局部变量将会消亡。

   
 

    单目非成员函数(操作数仅为后值),如取负。

1
inline complex

2
operator - (const complex& x, double y)

3
{

4
return complex (real (x) - y, imag (x));

5 }

    注:如果接口合适,非成员函数的操作符重载并不需要在类的声明中体现出来。

   
 

    *输入输出流相关函数的重载:(由于第一操作数为ostream对象,因此并不能作为成员函数。)

    
 

ostream & operator << (ostream& os, const complex &x)

{


return os<<'('<<real(x)<<','<<imag(x)<<')';

}

  

[GeekBand] C++学习笔记(1)——以复数类为例的更多相关文章

  1. java学习笔记07--日期操作类

    java学习笔记07--日期操作类   一.Date类 在java.util包中定义了Date类,Date类本身使用非常简单,直接输出其实例化对象即可. public class T { public ...

  2. Java程序猿的JavaScript学习笔记(10—— jQuery-在“类”层面扩展)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  3. AJPFX学习笔记JavaAPI之String类

    学习笔记JavaAPI之String类 [size=10.5000pt]一.所属包java.lang.String,没有子类.特点:一旦被初始化就不可以被改变. 创建类对象的两种方式: String ...

  4. 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理

    在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...

  5. JVM学习笔记-第六章-类文件结构

    JVM学习笔记-第六章-类文件结构 6.3 Class类文件的结构 本章中,笔者只是通俗地将任意一个有效的类或接口锁应当满足的格式称为"Class文件格式",实际上它完全不需要以磁 ...

  6. [GeekBand] C++学习笔记(2)——BigThree、OOP

    本篇笔记主要分为三个部分,第一部分是以String类为例的基于对象的编程,重点在于构造与析构.拷贝构造函数.拷贝赋值函数三个重要函数.这一部分与笔记(1)中的内容结合起来就是基于对象编程的主要内容.第 ...

  7. java学习笔记之日期日历类

    java学习笔记之日期日历 Date日期类概述: 表示特定的瞬间,精确到毫秒 Date类的构造方法: 1.空参数构造方法 Date date = new Date(); 获取到当前操作系统中的时间和日 ...

  8. 1.2(Spring MVC学习笔记) Spring MVC核心类及注解

    一.DispatcherServlet DispatcherServlet在程序中充当着前端控制器的作用,使用时只需在web.xml下配置即可. 配置格式如下: <?xml version=&q ...

  9. Python学习笔记(一)类和继承的使用

    一年前就打算学Python了,折腾来折腾去也一直没有用熟练,主要是类那一块不熟,昨天用Python写了几个网络编程的示例,感觉一下子迈进了很多.这几天把学习Python的笔记整理一下,内容尽量简洁. ...

随机推荐

  1. Android开发日志问题

    以前在Android开发中发现,日志打印好多,调试的时候各种加Log,之后就各种不删除,导致项目后期花大把时间删除日志打印. 学到一个好方法: 在所有尽可能高的父类里面加上一个常量 DEBUG ,一开 ...

  2. mysql 在线修改表结构工具 gh-ost

    gh-ost使用测试: gh-ost -host='192.168.65.136' -user=root -password='' -database='haha' -chunk-size=10000 ...

  3. Android adt v22.6.2-1085508 自己主动创建 appcompat_v7 解决方法,最低版本号2.2也不会出现

    Android 开发工具升级到22.6.2在创建project时仅仅要选择的最低版本号低于4.0,就会自己主动生成一个项目appcompat_v7,没创建一个新的项目都会自己主动创建,非常是烦恼... ...

  4. Linux lsof命令使用小结

        lsof(list open files)是一个列出当前系统打开文件的工具.在Linux环境下,任何事物都是以文件的形式存在,通过文件不仅可以访问常规数据,还可以访问网络连接和硬件.所以,如传 ...

  5. Linux系统常用命令 __转载的

    1.登录linux系统命令:login 用户名   密码: 2.注销linux系统命令:logout ; 3.在linux系统中进入windows系统(图形界面)命令:Start x; 4.关闭lin ...

  6. velocity properties

    resource.loader=webapp webapp.resource.loader.class=org.apache.velocity.tools.view.servlet.WebappLoa ...

  7. java_jdbc_batch处理_主键id获取

    //create1 速度较慢,create2较快,但是要根据数据库不同来决定 //ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_ ...

  8. 使用TortoiseGit对Git版本进行分支操作

    版本克隆分支问题 TortoiseGit在克隆分支的时候,默认克隆master分支,克隆后本地工作目录为中心器的Master分支. 克隆后本地分支 中心库分支 Push分支到中心服务器(Pushing ...

  9. Views

    Views Views are the visual side of the Nova, they are the HTML output of the pages. Views can be loc ...

  10. 通过pod导入第三方框架

    项目导入第三方框架的时候,如果直接拖到项目中的话还需要去修改某些设置以及导入头文件等,当项目比较大的时候这个方法就比较笨拙了,我们可以通过pod来简化这写流程: 在使用pod之前我们需要安装Cocoa ...