GeekBand C++ Week1 Notes
A.OOP-面向对象编程
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里的值或函数。
在这第一个学期的学习里,知道了写头文件的时候的一些约定俗成的方法和段落,以及定义一个类和方法的一些规则。