GeekBand C++ Week1 Notes

字数 4381阅读 161

GeekBand C++ Week1 Notes

AOOP-面向对象编程

1基础:C语言

-变量variable

-类型types

-作用域scope

-循环loops

-流程控制

-程序的编译和连结(如何运行)

2目标编程习惯

C++class

Objectbased基于对象,单个类

-Classwithout pointer members (complex)

-classwith pointer members (string)

Objectoriented面向对象,类之间的关系

-inheritance继承

-composition复合??

-delegation委托??

多态??封装??

三个例子

Complex.h complex-test.cpp

String.h string-test.cpp

Oop-demo.h oop-test.cpp

C++历史

B语言69,C语言72,C++83

OO:java, C#

C++98(1.0),C++03(TR1),C++11(2.0),C++14

加的新特性讲的C++98??????

C++语言(主要谈)C++标准库(庞大有用)

书:C++ primer,C++ programming language

C++standard library STL源码剖析

B.头文件和类声明

C语言和C++的比较

C数据和函数的变量是全局的(没封装的坏处)

C++数据和函数(方法)包在一起->建立很多对象

类里面带指针和不带指针:

不带指针:复数类,数据有实部和虚部,方法有加减乘除等

带指针:字符串类,字符串是一个指针(另外分配空间存放字符串),方法有拷贝输出附加等。

C++里面创建对象的方式

C++代码基本形式:

.h headfiles

classes declaration

.cpp

#inlcude//<>从标准库里找

#include”complex.h”//””从自己写的里面找

ex.main()

延伸名称不一定是.h和.cpp,不同平台

C++ #include

Cout << “I = “ << I <

C#include

Printf(“”)

头文件的写法

Complex.h

#ifndef ___COMPLEX__

#define __COMPLEX__//guard,防卫式声明

//***********0,声明************//

#include

class ostream;

class comlex;

complex&

__doap1(complex* ths, const complex& r);

//*********1,类和声明********//

class complex{

}

//********2,类,定义*********//

complex::function …

#endif

第二次include时可以skip,防止重复代码DRY!

1和2最重要,0是为了声明

语法:复数类的声明,模板的应用

template

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:

Tre, im;

friendcomplex& __doap1(complex*, const complex&);

}

template是模板,可以改变类中的某些值?已定义的变量名,可以改变值?还是需要用方法改变。

调用时,

complex c1(2.5, 1.5);

complex c2(2, 6);

C. class的声明

Complex& operator,没有大括号的方法内容,只是一个声明。

Complex& operator += (constcomplex&);

Inline函数:如果函数在class内定义完成是inline函数(内联函数)

Double real () const {return re;}

Double imag () const {return im;}

Inline function会比较快比较好。但没有可能所有函数都是inline,函数太复杂无法inline???是不是Inline由编译器决定。。。上面两个函数在body内定义则为inline function但最后是不是真的是由编译器决定。

Inline double

Imag(const complex& x){

Returnx.imag ();

}

后面出现的不在本体的部分也可以加inline?????

Access level访问级别

在类声明中可以分为public和private。

什么要放在private?数据部分!为了封装起来,函数要看具体,还有protected。段落可以任意出现也可以交错,比如有好几个public。

数据都应该放在private,函数内部处理可放在private,被外界调用可放在Public.

构造函数,自动调用(constructor)

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

:re{r}, im(i)

{ }

complex c1(2,1);

complex c2;

complex* p = new complex(4);

-构造函数名称一定和函数名称相同

-一定有参数

-参数可以有默认值double r = 0,double I = 0

-其他函数也可以有默认值!!!defaultargument

-构造函数没有返回类型

-赋值部分不要写在大括号里,利用只有构造函数可以用的语法

: re (r), im (i)初值列,初始列和赋值re = r; im

= I;一样的意义。

数值的设定有两阶段,一个是初始化,一个是赋值,所以使用这种更为有意义??

写在下面相当于放弃了初始化的阶段。

-析构函数,不带指针的类多半不需要写析构函数

构造函数可以有很多个。C++允许同一个函数多个出现,overloading因为输入参数不同

有很多种初值的设定。

Double real () const {return re;}

Void real (double r) {re = r;}

编译器编译后的实际名称可能是:

?real@Complex@@QBENXZ

?real@Complex@@ZARNABN@Z

新加的构造函数

complex () : re(0), im(0) {}

这样是不行的!!!!!!!

因为第一个构造函数有默认值

complex c1;

complex c2();

编译器会发现两个构造函数都可以调用,所以会报错。

-函数可以重载,输入类型不同

-函数重载常常会发生在构造函数上。

-像上面那样有默认值的如果两个同时可以调用就不行。

D参数传递与返回值

构造函数放在private区域会怎么样??

此时complex c1(2,1);

complex c2;就都不对

什么时候要这样做?不允许被外界创造对象。

设计模式Singleton

class A{

public:

staticA& getInstance();

setup(){}

private:

A();

A(constA& rhs);

}

A& A::getInstance(){

Static A a;

Returna;

}

A::getInstance().setup();

把构造函数写在private里叫Singleton

函数部分

double real () const {return re;}

double imag () const {return im;}

const的位置!!!!!

这两个函数不改变值,class里的函数有改变数据内容和不改变数据内容,不改变数据内容的函数马上加const。不加的后果:

complex c1(2, 1);

cout << c1.real();

cout << c1.imag();

const complex c1(2,1);

cout << c1.real();

cout << c1.imag();

下面的情况,complex是一个常数不可以改变,在用两个函数去调用的时候,如果函数里没写const说明函数可能会改data,编译器就会报错。Const可以用来修饰函数,可以用来修饰变量。

参数传递,pass by value vs. pass by reference(to const)

complex (double r = 0, double I = 0) passby value

complex & operator += (constcomplex&); pass by ref to const

operator << (ostream & os, constcomplex& x) pass by ref, pass by ref to const

-pass by value是把整个值传过去,几个字节就几个字节。

-传引用就相当于传指针那么快,形式很漂亮。

Complex c1(2,1);

Complex c2;

C2 += c1;

Cout << c2;

-最好所有参数的传递都传引用,字节固定,四个字节。

传引用,你改完会影响我。所以要看具体情况,但如果传过去只是为了速度,但不希望你改了会影响我,这时候可以用const.保证传给你后你不能改,你改了会影响我,此时如果传过去后被改了编译器会出错。

-参数传递尽量都传引用。

-如果想的细,比引用字节少,也可以传值。

Operator << (ostream& os, constcomplex& x)

第一个没有const说明允许改。

返回值传递return by value vs. return reference(to const)

coplex& operator += (constcomplex&);

double real () const {return re;}

double imag() const {return im;}

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

返回值尽量也传ref,在可以的情况下。

Friend(友元)

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

朋友可以来拿数据。

Private外界不可以接触数据

Inline complex&

__doapl (coplex* ths, const complex&r){

ths->re+= r.re;

ths->im+= r.im;

return*ths;

}

相同class的各个objects互为友元

int func(const complex& param)

{return param.re + param.im;}

当类里的函数输入类型是该类的话可以直接拿里面的数据,为什么??

看起来打破了封装,但是成立的。

Complex c1(2,1);

Complex c2;

C2.func(c1);

Class body外的各种定义definitions

-数据一定放在private里

-参数尽可能以ref来传,要不要加const看状况

-返回值尽量也用ref来传

-类的body里要加const的地方要加。

-构造函数initialization

list,冒号

什么情况下不能return by ref呢?

Inline complex&

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

ths->re+= r.re;

ths->im+= r.im;

return *ths;

}

加出来的结果是放在新创建来的地方还是放在已有的地方,如果是新生成的,在函数返回的时候就消失了,此时不能使用ref。除了这种情况都可以返回ref

inline complex&

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

return __doapl(this, r)

}

E.操作符重载与临时对象

头文件的防卫式声明,

C++中允许对操作符进行重载,比如让加法支持复数。

成员函数:this

c2 += c1

+=是一个二元操作符,编译器看到后会作用在左边上,

inline complex&

complex::operator += (this, constcomplex& x){

return __doapl (this, r);

}

this是隐藏的部分,不写,所有的成员参数一定带有一个隐藏的参数,叫this,谁调用这个函数谁就是this,这里c2就是this,this是一个指针,编译器会自动把c2的地址传进来当做this,写代码的时候不必写也不能写this,不影响我们写代码,不能再参数列里写出来,但在函数中可以用。

Return by ref语法分析

传递者不需要知道接受者是以ref形式接受的。

Inline complex&

__doapl(complex* ths, const complex&){

return *ths;

}

&写在后面和写在前面不一样,在后面是取得来的value的地址。

+=是将后面的值加到前面,所以理论上一个+=运算符是可以返回void,但如果使用c3 +=

c2 +=c1就会有问题,所以返回complex&更合理,可以进行多步操作。

所有的上面都是头文件里面类的定义部分

class complex{

}

下面是第二部分,成员函数和全域函数

inline double

imag(const complex& x){

return x.imag();

}

操作符冲重载非成员函数

c2 = c1 + c2;

c2 = c1 +5;

c2 = 7 + c1;

编译器回去找调用哪个

三种写法,所以要有三个函数

inline complex

operator + (const complex& x, constcomplex& y){

return complex((real(x) + real (y), imag(x) + imag(y));

}//复数加复数

全域函数,没有this pointer???

这三个函数都是返回value,为什么不用ref返回?

他们返回的不能是ref,因为他们返回的必定是个local object,之前是右边加到左边,左边已经存在,现在需要先创建一个左边的空间来存放数据,离开该函数后这个地址就失效了。叫temp object。

在上面函数中都没有定义complex,return complex();

typename();

这样的做法是创建临时对象,它的生命到下一行就结束了。

比如

complex();

complex(4, 5);

cout << complex(2);

比较特殊,一般人少用,但标准库很常用。

Negate

Inline complex

Operator + const complex& x){

Return x;

}

inline complex

operator – (const complex& x){

returncomplex (-real (x), -imag (x));

}

区别由输入的参数不同来区别,

negate返回的是一个新的东西,所以不能返回ref,但是上面的正好表示什么都不变,所以并没有产生新的结果,所以对于正号操作应该可以传回ref。

==

cout << (c1 == c2);

cout << (c1 == 2);

cout << (2 == c1);

考量输入有没有用ref,返回能否使用ref,

共轭复数,实部相等,虚部符号相反,

inline complex

conj (const complex& x){

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

}

任何一个函数都可以有两种想法,全域函数或者成员函数。

#include

ostream&

operator << (ostream& os, constcomplex& x){

return os<< ‘(’ << real (x) << ‘,’ << imag (x) << ‘)’;

}

cout << conj(c1);

对于output operator绝对不能写成一个成员函数

cout << c1 << conj(c1);

不能加const,因为ostream& os需要改变,所以不能加。按照道理,输出<<后面的值赋给前面后就没事了,所以可以返回值是void,但是当像上面一样连用后就不能返回void了。再去考虑返回的是by value还是by ref,发现返回的是原来的东西,所以用ref,再考虑要不要加const,发现改变了结果,所以不能加const。

重点复习

-构造函数的initialization:re(r), im(i)

-函数要不要加const

-参数的传递尽量考虑pass by ref,而且要不要加const要考虑

-return的时候是ref还是value

-你的数据几乎没有例外要放在private,而函数大部分要放在public,因为函数大部分要被外界调用。

Complex类

Week1总结

在写一个date类的过程中,我的写过程:

先声明宏定义,这个定义是为了防止重复引用头文件,在宏的名称命名规则上,系统内的宏一般以下划线或者双下划线开头,在我们自己定义宏的时候,尽量避免使用下划线开头,此处使用MYDATE

然后,到了视频中所说的头文件的第0部分,主要有下面要定义的类的声明和全局函数的声明。较为简单。然后到了头文件的第2部分,类的声明和类内成员函数的声明,注意的地方富有:在写构造函数的时候,使用数据的初始化,具体原理为在构造一个新的函数时,有初始化和赋值两部分组成,写在此处的: re(r), im(m) { }是一个初始化的过程,可以将两部完整的分开,更加合理。后面的大括号里面可能是一些文件的读写过程。这个初始化的方法是类内独有的,注意不要在其他地方使用。在写类内成员函数的时候,需要注意的有输入是否传引用还是传值,包括返回值是传引用还是传值。规则是,当输入基本都可以传引用,但如果分的细在小于4字节的值传递时传值更加有效率,返回值当传回的值为新建的时候需要传值(临时建立,结束函数后即消失),原有的输入更改时传引用。除此以外,在函数前面要注意是否改变类内的值,如果没有改变要加const。具体还有如果将构造函数写在private里是singleton的设计模式,在某些只允许有一个对象存在时可以使用,使用singleton设计模式时要注意通过成员函数将对象传出。

再到函数的第三部分

重构成员函数和全局函数,第一返回值前面可以加inline表示当函数比较小时,具体定义方法在类内就定义好调用时可以增加效率,但具体能不能inline是编译器决定的.

在写date函数时,有一个createpoints函数需要随机生成10个日期,在这个过程中我遇到了如何随机生成值得问题,使用了srand函数和rand()函数,每当使用srand设定一次种子,rand()函数重新生成会得到同样的值,导致一开始我在每次随机时都srand一次,得到了10个同样的值,后来只在一开始srand,每次rand()的值就不一样了。

再是在排序的时候我使用了冒泡排序,当找到最小值后,我没有想就写了将一个日期等于另一个日期,编译后发现不对,对于新建立的类内没有定义=的用法,需要在类内增加一个对于date类的=用法或者设定一个新的方法可以将一个对象的值赋给另一个对象,后来我设计了setvalue的方法用于赋值。

现在我遗留的问题有,在对complex类的例子里对于友元friend的设定可以是一个函数的意思是吗?因为以前只有接触过定义某个类是这个类的友元,则其private里的函数和值可以被其他类内直接调用,不知道我的理解对不对,即对某个函数定义为该类的友元,则在该全局函数里可以调用类内的private里的值或函数。

在这第一个学期的学习里,知道了写头文件的时候的一些约定俗成的方法和段落,以及定义一个类和方法的一些规则。

推荐阅读更多精彩内容