侯捷《C++面向对象编程》笔记第(一)周——超经典
1.推荐书籍
《EFFECTIVE C++改善55个方法》
《STL源码剖析》
《THE C++ STANDARN LIBRARY 》
《C++ Prinmer》
2.头文件与类声明
(1)
类根据里面是否包含指针,分为object based(面向单一class设计) and object oriented(面对多重class的设计,classes和classes之间的关系);本课程将分别就两种类型举例,分别写一个complex类(复数),和string类(字符串)。
(2)头文件防卫式声明
防止再次调用头文件的时候,程序重复访问头文件主体
#ifndef __COMPLEX__//第一次没有定义该类型,则定义并访问主体,如果第二次访问,则不需要再访问,可用于所有头文件文件
#define __COMPLEX__
class complex
{
......
}
#endif
(3)头文件布局
a. class template(模板)简介(开眼界了)
如果你的类里面的变量类型暂时还没确定,或者说你想随机应变,一会儿是int,下一个又可能是double 等等,可以用模板来泛化定义,使用的时候再定义
template <typename> T
class comples
public:
{
complex (T r=0, T i=0):re(r), im(i){}
}
private:{ T re, im;}
//main.cpp
complex<double > c1(2.2,4.3);
complex<int > c2(1, 3);
3.构造函数
(1)inline(内建)函数
定义:在class本体内定义的函数称为inline function(但最后是否真的是内联函数,由编译器决定,我们未知,一般太复杂的函数不会被认定为Inline)
(2)访问级别
private:数据或函数部分,不想让外界看到的数据,封装起来;
public:让外界看到。两者可以在一个类body内多次使用
protected:~
(3)构造函数
a.一旦定义,自动启动;
b.构造函数(其他函数也可以)可以有默认值,如果创建的时候未定义,则用default argument;
c.构造函数不需要返回类型;
d.不带指针的类,多半不需要写析构函数;
e.构造函数(其他函数也可以)常常用overloading(重载),用于多种初值设定想法;
class complex
{
public:
complex(double r=0, double i=0):re(r), im(i) {}//构造函数独有的初始化写法,更大气,等同于在{}里将传值一一赋给参数。这种赋值效率更高
/*
complex (double r=0, double i=0)
{re=r; im=i;}
*/
//构造函数常常被重载,虽然函数名在编码者看来看似相同,但在编译器看来是不同的
//如果第一个构造函数已经有默认值,下面这个重载的构造函数是不允许的,因为在新建一个不指定值的类时,编译器会不清楚应该用第一个还是下面这个,因为两个都可以
//complex (): re(0), im(0) {}
complex & operator +={const complex &};
double real() const { return re;}
double imag() const { return im;}
private:
double re, im;
friend complex& _doap1 (complex *, const complex &);
};
//main.cpp
{
//新建类的几种类型
complex c1(2,1);
complex c2;
complex *p = new complex(4);
}
4.参数传递与返回值
(1)ctors 放在private区
存在这种写法,叫Singleton
(2) 常量成员函数
a. 重要!!!在函数后,对于不会改变数据值的函数要加const。如果不加,遇到以下情况,编译器会报错!
class complex
{
public:
complex(double r=0, double i=0):re(r), im(i) {}
double real() const { return re;}//函数不会改变数据大小,const位置在函数定义后,在大括号前面
double imag() const { return im;}//同上
private:
double re, im;
};
//main.cpp
{
complex c1(2,1);
cout<<c1.real();
cout<<c1.imag();
//使用者如上使用没有问题,但是下列使用则会报错
const complex c1(2,1);
cout<<c1.real();//error!因为使用者想定义一个常量,也就是值不能被改变,但是该句调用时,
//如果原类函数没有加const,那么就说明它可能会改变数据的值,但是我前面又定义了常量,不能改变数据值,
//所以编译器就会报错。解决办法就是在不改变值的函数定义时都加上 const。
cout<<c1.imag();
}
(3)参数传递 :pass by value vs. pass by reference (to const)
a. 参数传递时,尽量传引用。引用在底部就是一个指针,所以传值很快。良好习惯:所有传值都尽量用引用。
(32位编译器:指针4个字节,字符1个字节,整型4个字节,double 8个字节,float 4个字节)
b.传参数引用的同时,不想自身的值被改变时,加const
class complex
{
public:
//...
complex& operator += (const complex&);//这里的参数传值选择的引用,并且由于不想参数在此函数中
//被改变,所以选择加const.
private:
};
//main.cpp
ostream&
operator <<(ostream& os, const complex& x)//同理,注意第一个参数未加const,原因后续道来
{
return os<<'('<<real(x)<<','<<imag(x)<<')';
}
c.return by value vs. return by reference 返回值也尽量传引用
complex& operator += (const complex&); //return by reference
double real() const{ return re; } //return by value
double imag() const{ return im; } // return by value
d.传引用也只是尽量,不一定所有场合都适用,后续会讲。
e.友元。对于其他外界函数,不能调用private成员,但是对于友元函数,就允许用private里的数据
private:
double re, im;
friend complex& _doap1 (complex *, const complex &);//友元函数
};
inline complex&
__doap1 (complex * ths, const complex& r)
{
ths->re += r.re;
ths->im +=r.im;
return *ths;
}
f. 相同class的各个objects 互为friends(意思就是类本体里面的函数可以调用private成员,而这样可行的原因就是前面所叙述的那样)
class complex
{
public:
//...
int func(const complex& param)
{ return param.re + param.im; }//该成员可以调用私有变量,是因为其默认为友元函数
private:
double re,im;
};
g. 总结:
数据要放在private里;
参数尽量以reference传;
是否加const; 返回值也尽量以reference传(允许的情况下);
类的本体里的函数应该加const的要记得加;
构造函数的初始化的专业写法。
h.补充:class body 外的各种定义:什么情况下可以pass by reference 什么情况下可以 return by reference
不能pass by reference 的情况:函数内部会将变量改变,则不加const;函数不改变变量的时候,则加const;
complex & ___doap1(complex* ths, const complex& r)//第一个变量在函数内部会被改变,所以不加const, 第二个不会被改变,所以加const.
{
ths->re +=r.re;
ths->im +=r.im;
return *ths;
}
不能return by reference的情况:返回值是在函数内部定义的一个变量则不能,因为该变量会随着函数结束而消失。若返回值是已经存在的,则可以返回引用。
complex& ___doap1(complex* ths, const complex& r)//文件名的 & 表明返回的是引用
{
ths->re +=r.re;
ths->im +=r.im;
return *ths;//因为ths是外部已经存在的变量,所以可以返回引用
//下列情况则不能传引用
/*
complex mm;
mm = ths->re + r.re;
return mm;//mm会随着函数结束而消失,所以不能传引用
*/
}
5.操作符重载与临时对象
(1)操作符重载-1,成员函数
a. 比如重载 += 操作符,首先我们要知道所有的成员函数一定带有一个隐藏的参数this, 即指向它本身的一个指针,这个指针在函数定义的时候不能出现,但在函数内部可以直接使用,指向的就是调用该函数的那个变量。如 c2 += c1; 则操作符重载中this 指向c2。
inline complex&
complex::operator += (const complex& r)//重载
{
return __doap1 (this, r);//this 默认指向使用的该符号的变量
}
complex & __doap1(complex* ths, const complex& r)//这个函数也可以直接在上面函数定义,但为防止以后还会继续用,则拎出来单独定义。
{
ths->re +=r.re;
ths->im +=r.im;
return *ths;
}//所有左右形式的符号,都可以这样定义
//main.cpp
complex c1(2,1);
complex c2(2,3);
c2 += c1;//+=操作符需要重载
b. 传递者无需知道接受者是以reference形式接受
complex & __doap1(complex* ths, const complex& r)//开头的& 是接受端
{
ths->re +=r.re;
ths->im +=r.im;
return *ths;//可以看到返回的是指针指向的值,但是接收端是返回的引用,但是我们可以不必在意,这样是可行的
}
c. 对于c3+=c2+=c1的考量,函数返回不能设为void
complex& __doap1(complex* ths, const complex& r)//开始的complex不能为void,如果只是c1+=c2,这样的操作是可以的,c2的值得到了改变,但是如果c1+=c2+=c3这样的操作就会出错,因为c3和c2加完后没有返回值,无法继续和c1相加。
{
ths->re +=r.re;
ths->im +=r.im;
return *ths;
}
e class body之外的各种定义,成员函数(函数前面带类的名称 complex::),全域函数(函数名前面没有类名)
(2)操作符重载-2,非成员函数
a. 为了应对使用者的几种可能做法,对应开发三个函数;
b.再次提醒:以下这些重载函数绝不能return by reference,因为传递的是函数内新建的临时对象;
c.特殊语法(临时对象): typename( c );内部也可以加减,相当于定义了一个新的该类型变量并赋值。它的生命到下一行就会结束,并且没有名称。
inline complex
operator + (const complex& x, const complex& y)//重载复数加复数
{
return complex (real(x)+real(y),
imag(x)+ imag(y));//临时对象,生命下一行就会结束,但是因为已经将值return,所以可行。
}
inline complex
operator + (const complex& x, double y)//重载复数加浮点数
{
return complex (real (x) + y, imag (x));//新建临时对象
}
inline complex
operator + (double x, const complex& y)//重载浮点数加复数
{
return complex (x+ real (y), imag (y) );//新建临时对象
}
//main.cpp
{
complex c1(2,1);
complex c2;
c2 = c1+c2;//复数加复数
c2=c1+5;//复数加浮点数
c2=7+c1;//浮点数加复数
//......
//临时对象举例
complex();//新建一个值为default value的临时对象,下一行就会消失;
complex(2,1);//新建一个实部为2,虚部为1的临时对象。
}
e. 操作符重载 正号、符号;编译器看参数个数来判断是正号还是加号
f. " << "输出符号的重载,只能作为全局函数来定义。"<<"的类型是"ostream"
# include <iostream.h>//该操作符重载必须是全局函数
ostream&//返回不能用void,因为有可能连续调用<<,如式3
operator << (ostream& os, const complex& x)//可以看到第一个参数不加const,因为每传一个值,const的状态就会被改变,所以它不是常量
{
return os<<'('<<real (x) <<','<<imag(x) <<')';
}
//main.cpp
{
complex c1(2,1);//1
cout<<conj(c1);//2
cout<<c1<<conj(c1);//3
}
第一周完!!!
posted on 2019-06-16 10:40 Nancy_Fighting 阅读(373) 评论(0) 编辑 收藏 举报