C++面向对象(二) Complex对象
C++面向对象(二)Complex类
- 一、Header(头文件)防卫式声明
- 二、Header(头文件)的布局
- class declaration & definition
- (1)内联函数——inline functions
- (2)构造函数——constructor(ctor)
- ① 初始化
- ② 重载(overloading)
- ③ 常量成员函数(==const== member functions)
- ④ 参数传递(pass by ==value== or ==reference(to const)==)
- ⑤ 返回值传递(return by ==value== or ==reference(to const)==)
- ⑥ 友元(friend)
一、Header(头文件)防卫式声明
complex.h
———————————————————————————————————————————
| #ifndef __COMPLEX__ |
| #define __COMPLEX__ |
| ——————————————————— |
| | | |
| | | |
| | ... | |
| | | |
| | | |
| | | |
| ——————————————————— |
| #endif |———————————————————————————————————————————
上面头文件内部要写入的内容,便是用来处理下面主程序main()函数部分的。
complex-text.cpp
#include <iostream>
#include "complex.h"
using namespace std;int main(){complex c1(2,1);complex c2;cout << c1 << endl;cout << c2 << endl;c2 = c1 + 5;c2 = 7 + C1;c2 = c1 + c2;c2 += c1;c2 += 3;c2 = -c1;cout << (c1 == c2) << endl;cout << (c1 != c2) << endl;cout << conj(c1) << endl;return 0;
}
二、Header(头文件)的布局
complex.h
———————————————————————————————————————————————————————————————————————————————| #ifndef __COMPLEX__ || #define __COMPLEX__ |———————————————————————————————————————————————————————————————————————————————
0 | #include <cmath> -------------------------- || | forward declarations | || class ostream; | (前置声明 ) | || class complex; -------------------------- || || complex& __doapl (complex* ths, const complex& r); |———————————————————————————————————————————————————————————————————————————————
1 | class complex{ ------------------------ || ... | class declarations | || }; | (类-声明 ) | || ------------------------ |———————————————————————————————————————————————————————————————————————————————
2 | complex::function ... ---------------------- || | class definition | || | (类-定义 ) | || ---------------------- |———————————————————————————————————————————————————————————————————————————————| #endif |———————————————————————————————————————————————————————————————————————————————
主要部分是头文件的类声明和类定义部分,而前置声明部分主要是为了给类声明和类定义部分提供一些前置变量声明的准备。
class declaration & definition
class complex{ (class head)
———————————————————————————————————————————————————————————————————————————————
public: (class body)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;}
private:double re, im;friend complex& __doaple(complex*, const complex&);
};
{
complex c1(2, 1);
complex c2;
}
由上面可知,
有些函数可在body里直接定义,如imag()、real(),
有些是先在body里声明,然后再在body外定义,如 ”+=“ 的重载、友元函数 __doaple() 。
一般来说,类对象的数据 建议 应该放在private内,防止被随意修改调用;而对于希望被外界使用的函数,则 建议 放在public内,方便外界调用类对象的函数。
同时,需注意,如果需设置另一个复数(complex)类型,其数据是由int型的re、im构成,如果按以上代码编写,则又需另外构造一个complex1类对象了(即将double →\rightarrow→ int);再换成数据由float型的re、im构成,则又需构造complex2…
所以引入 模板 template ,用一个类对象 complex 就可表示多种数据集成类型的对象。
---------------------------------------------------- ⇓\Downarrow⇓ -----------------------------------------------------------
template <typename T>
class complex{
public: complex(T r = 0, T i = 0): re(r), im(i) {}complex& operator += (const complex&);T real() const{return re;}T imag() const{return im;}
private:T re, im;friend complex& __doaple(complex*, const complex&);
};
{complex<double> c1(2.5, 1.5);complex<int> c2(2, 6);
}
在类对象前面先声明 template< typename T>,说明T是个type,是 未定的 (也可以换成其他的字母P、X等),等使用者如complex c1(2.5, 1.5) 需要使用 double类型 就将 T 绑定为对应的 double 类型。
(1)内联函数——inline functions
内联函数的优点是执行快 。而函数若在class body内定义完成,便自动成为inline的候选人,但不是一定的 ,是否可以成为inline函数仍需要编译器自己去选择判断。
分界点在:看函数是否复杂,不复杂则可转化为inlie函数。
在上面的例子中,因为real()、imag()函数是在class body里面定义的,且内容简单不复杂,所以其为inline函数。我们也可以选择不在class body内定义,而在body外定义real()、imag()为inline函数,只需在前面添加关键字 inline即可。但我们需要注意,我们添加inline关键字在函数前面,只是建议编译器尽量将其转为内联函数而已,但实际上是不是转为inline函数,是编译器自己去判断的。
inline double imag(const complex& x){return x.imag();
}
(2)构造函数——constructor(ctor)
构造函数也是可以放在private里面的
① 初始化
表达一(构造函数独有的表达形式):
complex(double r = 0, double i = 0) : re( r), im(i) {}
表达二:
complex(double r = 0, double i = 0) {
re = r;
im = i;
}
两种表达方式都可以达到完成赋值的效果,但表达方式一效率更高,简单一点分析的话,就是因为一个变量或对象其数值的设定有两个阶段:一是初始化阶段,二是赋值阶段 ,表达二中放弃了赋初值(初始化)的阶段,而选择在{}里再赋值,时间上晚了些,代码效率便也差了些。
② 重载(overloading)
C中是不允许相同函数名称却有多个的,而C++中是允许存在一个以上的相同函数名称的函数的(函数的重载)。
如 上面代码中,可同时存在同名函数real():
double real() const {
return re;
}
void real(double r){
re = r;
}
两者的实现功能不一样,前者是取出实部,后者是设定实部,且两者编译后的实际名称是不一样的:
?real@Complex@@QBENXZ
?real@Complex@@QAENABN@Z
但下面情况下,构造函数的同名函数重载是不能同时存在的,因为两者作用一样,编译器会无法判断到底该去调用哪一个函数:
complex(double r = 0, double i = 0) {
re = r;
im = i;
}
complex(double r = 0, double i = 0) : re( r ), im( i ) {}
- 操作符重载(成员函数,有this指针 )
每个成员函数都有个隐藏的this pointer(指针),其指向调用者,谁调用这个函数就指针就指向谁
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);
}int main(){complex c1(2, 1);complex c2(5);c2 += c1;
}
这里实际上在重载操作符 inline complex& complex::operator += (const complex& r) 时,括号里面的参数隐藏了个this指针的,其将c2的地址赋给this指针,将c1赋给r 。
inline complex& complex::operator += (“this”, const complex& r){
return __doapl(this, r);
}
- 操作符重载(非成员函数,无this指针 )
{complex c1(2, 1);complex c2;
}
c2 = c1 + c2;
inline complex operator + (const complex& x, const complex& y){return complex(real(x) + real(y), imag(x) + imag(y));
}
c2 = c1 + 5;
inline complex operator + (const complex& x, double y){return complex(real(x) + y, imag(x));
}
c2 = 7 + c1;
inline complex operator + (double x, const complex& y){return complex(x + real(y), imag(y));
}
注意:上面这些重载函数是不可return by reference,因为他们返回值是个locall object,代码语句执行到下一行就消灭掉了,所以返回值引用传递的话会出错。其操作将两个objects相加起来,然后在函数体内创建出新的原本不存在的空间去存放相加的结果,在函数里才创建的新东西,其离开函数就会死亡了。
而上面函数内所创建的新的临时对象,是通过 typename() 的形式创建的,这种 temp object ,没有名称,只由 类型+() 组成,一旦函数运行结束便死亡了。如:
int(7);
…
return complex(real(x) + y, imag(x));
下面c1、c2不是临时对象,而 complex() 、complex(4, 5) 、cout << complex(2) 均是临时对象。
complex c1(2, 1);
complex c2;
complex();
complex(4, 5);
cout << complex(2);
对于特殊的操作符,重载函数应为 全局函数(global) 。
#include <iostream.h>
ostream& operator << (ostream& os, const complex& x){return os << '(' << real(x) << ',' << imag(x) << ')';
}
{
complex c1(2, 1);
cout << c1 << complex(2);
}
上面对于重载的<<,先看返回类型能不能用引用传递,因为 cout是已经存在的object ,所以返回值传递可以使用引用传递。再查手册知,cout是ostream类,且cout是会被修改的,所以参数传递时用 ostream& 而不是 const ostream& 。
同时返回值传递时不能在函数前加const限定 (即const ostream& operator <<(…)) ,且也不能设置函数类型为空 (即void operator <<(…)) ,因为 cout << c1 执行完后需要返回对应的ostream& ,然后才可以接收后续的 (结果)<< complex(2) 。
#include <iostream.h>
void operator << (ostream& os, const complex& x){os << '(' << real(x) << ',' << imag(x) << ')';
}
{
complex c1(2, 1);
cout << c1; 可以正确运行
cout << complex(2); 可以正确运行
cout << c1 << complex(2); 错误!不可正确运行
}
③ 常量成员函数(const member functions)
class里面的有 会改变数据的 和 不会改变数据的 两种函数,若为 不会改变数据的 则需 在函数的后头加const 。
double real() const {return re;
}
此时,以下两种创建对象的代码都可以正常运行:
{
complex c1(2, 1);
cout << c1.real();
}
{
const complex c1(2, 1);
cout << c1.real();
}
但若函数不加 const限定 ,而所创建对象又为常量对象,则编译器会报错:
double real() {return re;
}
此时,第一种创建对象代码可以正常运行,而第二种则编译器会报错:
{
complex c1(2, 1);
cout << c1.real();
}
{
const complex c1(2, 1);
cout << c1.real();
}
错误!
④ 参数传递(pass by value or reference(to const))
一般来说,引用传递 比 值传递 快,因为引用传递相当于传递的是指针,操作效率更高(但不是说所有情况下都是 引用传比值传快 ,因为如果只是传一个1字节大小的字符,值传递此时只需传输一个字节,而引用传递则需像指针传递一样需要传输4字节)。所以一般情况下,均建议使用引用传递,提高代码效率。
引用传递因为像指针一样,若在操作中对引用传递的对象进行了修改,则相应的原来被引对象也会发生变化,若不想在传递过程中造成被引对象的变化,则需在加 关键字const 限定。
⑤ 返回值传递(return by value or reference(to const))
complex& operator += (const complex&)
double real() const{
return re;
}
friend complex& __doapl(complex*, const complex&);
ostream& operator << (ostream& os, const complex& x){
return os << ‘(’ << real(x) << ‘,’ << imag(x) << ‘)’;
}
Ⅰ. 返回值引用传递时需要注意:
若函数内部处理后产生的结果,是放置在本来已经存在的空间上时,则可以使用返回值传递:
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);
}
若函数的操作结果或运算结果,是放置在本来还未存在的需要在函数里面创建出来的空间上时,是个locall变量,其返回的结果会随着函数结束本体就消亡掉了,执行到代码语句的下一行就消失掉了,所以不可使用返回值传递:
inline complex complex::operator + (const complex& x, const complex& y){return (x.real() + y.real());
}
同样的,对比:
inline complex& operator + (const complex& x){return x;
}
inline complex operator - (const complex& x){return complex(-real(x), -imag(x));
}
前者可使用返回值引用传递,但后者不可。
Ⅱ. 传递者 无需知道 接收者 是以何种形式接收数据。
inline complex& __doapl(complex* ths, const complex& r){...return *ths;
}
上面代码中,*ths 是传递者,而 complex& 是接收者,即无论返回值是何种形式,都可用引用的方式接收返回值。
inline complex& complex::operator += (const complex& r){return __doapl(this, r);
}
int main(){complex c1(2, 1);complex c2(5);c2 += c1;
}
类似的,c1 是传递者,而 const complex& r 是接收者,也是采取了引用传递的方式接收了返回值。
Ⅲ. 连串使用时,需注意返回值类型不能空
c3 += c2 += c1;
此时,返回值类型即函数类型不能定义为void ,应定义为引用传递类型 complex& 。
因为其是相当于 c2 += c1运算的结果 ,再返回给 c3 进行操作即 c3 += (结果) :
inline complex& complex::operator += (const complex& r){return __doapl(this, r);
}
若只是:
c2 += c1;
则函数类型可定义为 void ,不影响:
inline void complex::operator += (const complex& r){return __doapl(this, r);
}
⑥ 友元(friend)
class中的friend函数可以自由取得private中的数据
template <typename T>
class complex{
public: complex(T r = 0, T i = 0): re(r), im(i) {}complex& operator += (const complex&);T real() const{return re;}T imag() const{return im;}
private:T re, im;friend complex& __doaple(complex*, const complex&);
};inline complex& __doapl(complex* ths, const complex& r){ths->re += r.re;ths->im += r.im;return *ths;
}
注意:同个class内的各个objects互为friends(友元)
template <typename T>
class complex{
public: complex(T r = 0, T i = 0): re(r), im(i) {}complex& operator += (const complex&);T func(const complex& param){return param.re + param.im;}
private:T re, im;
};int main(){complex<int> c1(2, 1);complex<int> c2;c2.func<int>(c1);
}
C++面向对象(二) Complex对象相关推荐
- 笔记整理2----Java语言基础(二)06 断点调试与数据加密+07 面向对象-类与对象+08 java常用API-基础+09 java集合+10 IO流-基础
06 断点调试与数据加密+07 面向对象-类与对象+08 java常用API-基础+09 java集合+10 IO流-基础 第06天 java基础语法 今日内容介绍 Eclipse断点调试 基 ...
- Python之面向对象类和对象
Python之面向对象类和对象 定义一个类:class 定义类的语法: class Test(object):"""类里定义一类事物共同的技能.可以是变量,也可是函数.& ...
- Javascript面向对象二
Javascript面向对象二 可以通过指定原型属性来对所有的对象指定属性, Object.prototype.name="zhangsan"; Object.prototype. ...
- Day07:常用模块,面向对象编程(对象类)及内置函数
今日内容: 1.常用模块 2.面向对象编程(*****) 介绍面向对象编程 类 对象 3.内置函数 ------------------------------ 1.面向过程编 ...
- Java面向对象-01-类和对象
一.面向对象概述 一.面向对象概述 在程序开发初期人们使用结构化开发语言,但是随着时间的流逝,软件的规模越来越庞大,结构化语言的弊端也逐渐暴露出来,开发周期被无休止的拖延,产品的质量也不尽如人意,人们 ...
- java面向对象-类和对象
Java面向对象笔记 文章目录 Java面向对象笔记 java面向对象 类和对象 什么是类 定义 创建和使用 公式 构造方法 什么是构造方 怎么定义和使用构造方法 实列 This关键字 什么是成员变量 ...
- 2. 星际争霸之php面向对象(二)
题记 ============================================================================== 本php设计模式专辑来源于博客(jy ...
- python面向对象 —— 类和对象(一)
文章目录 一.面向对象的概念 二.类和对象 (一)类 (二)对象 (三)从对象抽象出类 三.创建类 (一)类属性 类变量和实例变量 (二)类的方法 参数 :self 的理解 __init__(初始化方 ...
- Python015面向对象之类和对象
Python015面向对象之类和对象 记得第一次听说面向对象时,觉得很高大上,那时完全不懂什么是面向对象.上大二之前,用的编程思想是面向过程的,那时候还问过老师什么是面向对象,当时老师也是回答的很笼统 ...
- 面向对象-类和对象、构造方法、封装
目录 面向对象 一.类和对象 1.定义 2.关系 3.类的组成 4.创建对象的格式 5.使用对象成员变量的格式 6.使用对象成员方法的格式 二.对象内存图 1.单个对象内存图 2.两个对象内存图(方法 ...
最新文章
- Ant Design 被删代码已恢复,现登顶 GitHub Trending Top 2
- java字符生成器_Java实现简单字符生成器代码例子
- 新单词 part 4
- 查看linux服务器的系统信息
- 【HDU - 1024 】Max Sum Plus Plus (dp及优化,最大m子段和)
- java质数和合数的程序_《java项目实训》课程设计计算器.doc
- End-to-End Object Detection with Transformers的部分解读
- 我常用的那些 Git 命令
- vue-socket.io 对IE浏览器(IE10+)的兼容性修复
- STM32F103C8T6最小系统
- linux 找不到vmdk,找不到文件vmdk虚拟磁盘
- 星际争霸2 AI开发(持续更新)
- 【视频学习笔记】计算机视觉与深度学习_北京邮电大学_鲁鹏(⭐⭐⭐⭐⭐)
- Zuul网关拦截器配置
- 贪心(基于检索式问答系统)
- 【51nod】3121 小陶与杠铃片
- 最标准的系统字体规范font-family
- c++ 字符串匹配算法sunday算法
- 涡流核聚变反应堆项目
- Tampermonkey油猴脚本开发视频教程-3.脚本配置和权限申请UserScript