C++学习笔记_complex类的实现
头文件中的防卫式声明
点击查看代码
#ifndef __COMPLEX__
#define __COMPLEX__
class complex
{
}
#endif
类的定义
点击查看代码
class complex//class head
{
//class body
//有些函數在此直接定义,另一些在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; }
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};
模板类:
点击查看代码
template<typename T>//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& __doapl (complex*, const complex&);
};
访问级别
public :外部能看到的
private:只有自己能看到的,一般只有内部函数处理的数据要用private
protect:受保护的
构造函数
构造函数在创建的时候就被调用起来,如果指明,就用指明的参数,如果没有,就要用默认值为参数
点击查看代码
complex (double r = 0, double i = 0)//0为默认实参(default argument)
: re (r), im (i)//初值列,初始列,initialization list
{ }
初值化是只有构造函数享有的。
如上:re(r),im(i),效果和我们平时在大括号中赋值相同,即re=r,im=i;但是初值化效率更高,请充分利用这种特性。
不带指针的函数多半是不用写析构函数的。
c++容许有多个构造函数,但是上图这种新写的黄色加深的函数是不容许的,因为和最上面的构造函数相冲突,导致我们在构造的时候不知道用哪个构造函数来构造。
函数重载
函数名称一样,但是函数的参数个数或参数类型不一样或有无 const,与返回值类型无关。
单例模式中构造函数可能放到private中
点击查看代码
class A {
public:
static A& getInstance();
setup() { ... }
private:
A();
A(const A& rhs);
...
};
A& A::getInstance()
{
static A a;
return a;
}
如上图,在单例模式中,有一种需求,是把构造函数放到private中的,它不容许外界直接通过A a();来创建。
常量成员函数
当我们在设计接口的时候,这个函数要考虑到是否会对这个函数的返回值作出改变,如果不改变一定要加上const。
如果没加会造成什么后果呢,如果使用者在创建一个复数的对象时,声明这个对象时常量,是不可以被改变的,但是这个函数的内部中没有使用const,这就意味着这个函数有可能会被改变,那么编译器就会报错,使用者就会迷糊,为什么我定义了一个不可以被改变的常量都不能打印出来呢。虽然使用者在使用的时候可能不用const,但是我们要想的更周全。
参数传递
点击查看代码
class complex
{
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{ }
complex& operator += (const complex&);//pass by reference
double real () const { return re; }
double imag () const { return im; }
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};
参数传递有两种方式,一种是by Value, 还有一种是by reference
Value 顾名思义就是值,但是当我们的Value大小很大时,假如有几十Kb,也用Value效率很低,很不美观,所以我们可以用by reference的形式来传递,这就是传递的一个指针指向的地址的引用,这个地址中报存的值就是我们想要传进去的东西,这种方式美观大气,如上图c2+=c1,其中传的就是c1指针指向的地址的引用。
传指针的方式很快,如果我们只想传这个数值,但是后续的操作可能会将这个地址中的值改变,然而我们构建这个函数的时候只想让你得到,但是不想让你改怎么办呢,就要加上const。
所以在可以的情况下,我们能用by reference就多用by reference,这是一种高效的方法,是我们选择用c++来写程序的原因
返回值传递
同参数传递。
友元
在定义了友元函数后,友元函数可以直接拿到private中定义的数据
点击查看代码
class complex {
...
// 相同class的各个 objects 互为 friends(友员)
int func(const complex ¶m) {
return param.re + param.im; // 可直接访问 私有数据
}
private:
double re, im;
};
什么样的情况是不可以用by reference来返回的
如果是在函数中创建的对象,那么这个对象就不能用引用的方式来传递,理解为本体已经死掉了,这地址中的东西被回收,就不是我们要看的东西,就不可以传引用。
在之前的博客中有专门一章讲述在C++中为什么尽量在传参时用pass-by-reference取代pass-by-value,其主要目的是为了节省构造函数和析构函数的成本,但这不能完全的取代,假如有下面的一个类:
1 class Rational
2 {
3 public:
4 Rational(int numerator = 0, int denominator = 1);
5 private:
6 int n, d;
7 };
现在为此类添加成员函数operator*,不妨先找找以下的代码有什么问题:
const Rational& operator*(Rational& a, Rational& b)
{
Rational result(a.n*b.n, a.d*b.d);
return result;
}
这段代码有两个问题需要注意:第一 在函数内部定义了一个local对象,然后返回它的地址,而我们应该指导local对象会在退出函数时被销毁,所以如果用户使用这个reference指向的对象会出现”未定义错误“;第二 result的创建还是逃避不了构造函数调用的成本。
总结:
- 数据一定放在private中
- 构建函数的写法尽量用初值化写法
- 参数的传递能用引用就用引用(先考虑by reference)
- 类名相同时他们互为友元。
- 不希望改变的值一定要加const
- 构建函数可以有多个
- 函数重载只是我们看着名称一样,但是机器看是不同的。
操作符重载
this是这个编译器给我们的一个特殊的指针,这个指针从的是调用者的地址,如c2+=c1,“+=”在编译器眼里相当于一个函数,那么是谁来调用呢,c2,即this指针指向的就是c2。this一般是隐藏起来的,它不能在参数列写出来,但是你可以使用,它可能在参数区前面也可能在参数区后面。 同时我们要保存好习惯,c1我们是不希望改变的,所以要加上const 符号。
1、_doapl这个函数的返回值是一个value ,接收方是complex &,我们在用return by reference的时候传送的一方不需要知道接收者是by reference的方式接收的。
2、我们在设计函数的时候要考虑到连加连减的情况发生,虽然说这个里面参数的value已经发生变化。只是单一的运算是不需要返回值的,但是一旦连续运算,就会出错。
头文件的布局
typename()是临时对象temp object,没有名称,在它的下一行就会被释放吧,如上图所示,我们要传出去的是一个value, 这个complex类型的value就是用的临时对象语法
这里对“<<”进行操作符的重载,首先想到我们传进去的参数是否可以用by reference,好的,可以,这个值会不会被改变,这个参数是我们不想改变的,前面加上const,那么<<是作用到什么上的,作用到cout上,cout是什么,是一个ostream类型的东西,这个东西可不可以传引用,可以,这个东西我们要不要更改,因为每次输出都会改变状态,所以不能加const,它的返回有用吗,没有用,好像可以用来接收,但是如果有多个<<连着输出呢,好的不能用void,我们得返回一个相同类型的对象,这个可以用引用吗,可以。
incline 的使用说明:
incline 意为内联 其目的是提高函数执行效率。在 C程序中,可以用宏代码提高执行效率,内联函数相对于宏来说,增加了对类型的检查,使用起来更安全。宏代码本身不是函数,但使用起来象函数。预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的 CALL调用、返回参数、执行return等过程,从而提高了速度。使用宏代码最大的缺点是容易出错,预处理器在复制宏代码时常常产生意想不到的边际效应。对于任何内联函数,编译器在符号表里放入函数的声明(包括名字、参数类型、返回值类型)。如果编译器没有发现内联函数存在错误,那么该函数的代码也被放入符号表里。如果检查函数正确,内联函数的代码就会直接替换函数调用,于是省去了函数调用的开销。
C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作类的数据成员。所以在C++ 程序中,应该用内联函数取代所有宏代码,“断言assert”恐怕是唯一的例外。assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。如果assert是函数,由于函数调用会引起内存、代码的变动,那么将导致Debug版本与Release版本存在差异。所以assert不是函数,而是宏。
- 内联可调试
- 内联可进行安全类型检查或自动类型转换
- 可访问成员变量
- 定义在类声明中的成员函数自动转化为内联函数
内联函数的编程风格
关键字inline必须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前面不起任何作用。
//如下风格的函数Foo不能成为内联函数:
inline void Foo(int x, int y); // inline仅与函数声明放在一起
void Foo(int x, int y)
{
…
}
//而如下风格的函数Foo则成为内联函数:
void Foo(int x, int y);
inline void Foo(int x, int y) // inline与函数定义体放在一起
{
…
}
所以说,inline是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。一般地,用户可以阅读函数的声明,但是看不到函数的定义。尽管在大多数教科书中内联函数的声明、定义体前面都加了inline关键字,但我认为inline不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C程序设计风格的一个基本原则:声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。
适用范围:
1、如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
2.如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
点击查看代码
#ifndef _COMPLEX_
#define _COMPLEX_
#include <cmath>
#include <iostream>
using namespace std;
class Complex
{
private:
double re;
double im;
friend Complex& _doapl(Complex*,const Complex& );
friend Complex &__doami(Complex *, const Complex &);
friend Complex &__doaml(Complex *, const Complex &);
public:
Complex (double r = 0, double i = 0)
:re(r),im(i) {}
double real() const { return re; }
double imag() const { return im; }
Complex& operator+=(const Complex&c);
Complex& operator-=(const Complex&c);
Complex& operator*=(const Complex&c);
};
inline Complex &
Complex::operator += (const Complex& r){
return _doapl(this,r); //+=的操作符重载交给友元函数做
}
inline Complex &
Complex::operator-=(const Complex &r){
return __doami(this, r); //-=的操作符重载交给友元函数做
}
inline Complex &
Complex::operator*=(const Complex &r){
return __doaml(this, r); //*=的操作符重载交给友元函数做
}
inline Complex&
_doapl(Complex* ths,const Complex& r){ //实现+=的重载
ths->re += r.re;
ths->im += r.im;
return *ths;
}
inline Complex &
__doami(Complex *ths, const Complex &r) //实现-=的重载
{
ths->re -= r.re;
ths->im -= r.im;
return *ths;
}
inline Complex &
__doaml(Complex *ths, const Complex &r) //实现*=的重载
{
double f = ths->re * r.re - ths->im * r.im;
ths->im = ths->re * r.im + ths->im * r.re;
ths->re = f;
return *ths;
}
inline double
real(const Complex &r)
{
return r.real();
}
inline double
imag(const Complex &i)
{
return i.imag();
}
inline Complex
operator + (const Complex&l,const Complex&r){ //复数加复数
return Complex( real(l) + real(r), imag(l) + imag(r) );
}
inline Complex
operator + (const Complex&l, double r){ //复数+实数
return Complex( real(l)+r , imag(l));
}
inline Complex
operator + (double l,const Complex&r){ //实数加复数
return Complex(l+real(r) , imag(r) );
}
inline Complex
operator - (const Complex&l,const Complex&r){
return Complex(real(l) - real(r), imag(l) - imag(r) ); //复数-复数
}
inline Complex
operator - (const Complex&l, double r){ //复数-实数
return Complex( real(l)-r , imag(l) );
}
inline Complex
operator - (double l,const Complex&r){ //实数-复数
return Complex( l-real(r) , -imag(r) );
}
inline Complex
operator*(const Complex &left, const Complex &right)
{
return Complex(real(left) * real(right) - imag(left) * imag(right),
real(left) * imag(right) + imag(left) * real(right));
}
inline Complex
operator*(const Complex &left, double right)
{
return Complex(real(left) * right, imag(left) * right);
}
inline Complex
operator*(double left, const Complex &right)
{
return Complex(left * real(right), left * imag(right));
}
inline Complex
operator/(const Complex &left, double y)
{
return Complex(real(left) / y, imag(left) / y);
}
inline Complex
operator+(const Complex &x) // ??
{
return x;
}
inline Complex
operator-(const Complex &x)
{
return Complex(-real(x), -imag(x));
}
inline bool
operator==(const Complex &left, const Complex &right)
{
return real(left) == real(right) && imag(left) == imag(right);
}
inline bool
operator==(const Complex &left, double right)
{
return real(left) == right && imag(left) == 0;
}
inline bool
operator==(double left, const Complex &right)
{
return left == real(right) && 0 == imag(right);
}
inline bool
operator!=(const Complex &left, const Complex &right)
{
return real(left) != real(right) || imag(left) != imag(right);
}
inline bool
operator!=(const Complex &left, double right)
{
return real(left) != right || imag(left) != 0;
}
inline bool
operator!=(double left, const Complex &right)
{
return left != real(right) || 0 == imag(right);
}
inline ostream&
operator <<(ostream& o,const Complex& c){
return o<<c.real()<<","<<c.imag();
}
#endif
C++学习笔记_complex类的实现的更多相关文章
- Java学习笔记——File类之文件管理和读写操作、下载图片
Java学习笔记——File类之文件管理和读写操作.下载图片 File类的总结: 1.文件和文件夹的创建 2.文件的读取 3.文件的写入 4.文件的复制(字符流.字节流.处理流) 5.以图片地址下载图 ...
- python学习笔记4_类和更抽象
python学习笔记4_类和更抽象 一.对象 class 对象主要有三个特性,继承.封装.多态.python的核心. 1.多态.封装.继承 多态,就算不知道变量所引用的类型,还是可以操作对象,根据类型 ...
- Java学习笔记之---类和对象
Java学习笔记之---类和对象 (一)类 类是一个模板,它描述一类对象的行为和状态 例如:动物类是一个类,动物们都有属性:颜色,动物们都有行为:吃饭 public class Dog { Stri ...
- UML学习笔记:类图
UML学习笔记:类图 有些问题,不去解决,就永远都是问题! 类图 类图(Class Diagrame)是描述类.接口以及它们之间关系的图,用来显示系统中各个类的静态结构. 类图包含2种元素:类.接口, ...
- swift学习笔记3——类、结构体、枚举
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- Java学习笔记-File类的基本方法
要渐渐养成写博客的习惯-----> 前段时间看Mars的java中的I/O流没怎么懂,发现I/O流好难啊.今天重新看一遍其他教学,还有书籍,做些笔记,记录下每天的学习生活. File类的一些方法 ...
- CSS3学习笔记——伪类hover
最近看到一篇文章:“Transition.Transform和Animation使用简介及应用展示” ,想看看里面 “不同缓动类效果demo”例子的效果,发现了一个问题如下: .Trans_Bo ...
- Java7编程 高级进阶学习笔记--嵌套类
定义: 在一个类中定义的类叫做嵌套类. 作用: 1.允许对相关类进行逻辑分组 2.增强了代码的封装性 3.使代码具有更强的可读性和维护性 使用方式: package com.cmz.baseTest; ...
- 初探swift语言的学习笔记四(类对象,函数)
作者:fengsh998 原文地址:http://blog.csdn.net/fengsh998/article/details/29606137 转载请注明出处 假设认为文章对你有所帮助,请通过留言 ...
随机推荐
- Pytest_参数化(10)
pytest参数化有两种方式: mark的parametrize标记:@pytest.mark.parametrize(变量名,变量值),其中变量值类型为列表.元组或其它可迭代对象. fixture的 ...
- Django_模型类详解(七)
# 定义书籍模型类 class BookInfo(models.Model): btitle = models.CharField(max_length=20) # 书籍名称 bpub_date = ...
- Visual Studio Code快速补全html标签(Sublime同样支持)
1.生成html文件骨架 输入"!" 或 "html:5",按tab键 注意:编写中文网页,记得把头部语言<html lang="en" ...
- react组件性能优化PureComponent
首先我们使用react组件会配合connect来连接store获取state,那么只要store中的state发生改变组件就会重新渲染,所以性能不高,一般我们可以使用shouldComponentUp ...
- centos7 单用户模式修改root密码
1. 在虚拟机重启客户机后.会出现下面进入界面.按e键 2.按了e键后,会出现下面的界面.此时按↓键.找到linux16 3.将光标移动到UTF-8后面,添加init=/bin/sh,并按 ctrl ...
- Spark-2.0.2源码编译
注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6813925210731840013/ Spark官网下载地址: http://spark.apache.org/d ...
- vue3.0+vite+ts项目搭建--初始化项目(一)
vite 初始化项目 使用npm npm init vite@latest 使用yarn yarn create vite 使用pnpm pnpx create-vite 根据提示输入项目名称,选择v ...
- Maven+ajax+SSM实现查询
2.尚硅谷_SSM高级整合_使用ajax操作实现页面的查询功能 16.尚硅谷_SSM高级整合_查询_返回分页的json数据.avi 在上一章节的操作中我们是将PageInfo对象存储在request域 ...
- if结构题目记录
1.使用if结构实现:若年龄够7岁或者年龄够5岁并且性别为男,就可以搬动桌子 import java.util.Scanner; /** * 使用if结构实现:若年龄够7岁或者年龄够5岁并且性别为男, ...
- Android 12(S) 图形显示系统 - 基本概念(一)
1 前言 Android图形系统是系统框架中一个非常重要的子系统,与其它子系统一样,Android 框架提供了各种用于 2D 和 3D 图形渲染的 API供开发者使用来创建绚丽多彩的应用APP.图形渲 ...