C++ wk1学习笔记 面向对象程序设计

参考资料

C++程序设计 北京大学郭炜

(https://blog.csdn.net/tianxiaolu1175/article/details/46889523)

1.引用

引用的概念

引用(reference): 引用只是别名,不是实体类型(也就是说c++编译器不为引用单独分配内存空间),对一个对象的引用,就是直接对这个对象的操作。

下面的写法定义了一个引用,并将其初始化为引用某个变量。

类型名 & 引用名=某变量名;


int n = 4;  
int & r = n; //r引用了n,r的类型是 int &

示例1:

int n = 4;
int & r = n;
r = 4;
cout << r; //输出 4
cout << n; //输出 4    
n = 5;
cout << r; //输出 5

三个特性:

1.定义引用时一定要将其初始化成引用某个变量。

2.初始化后,它就一直在引用该变量,不会再引用其他别的变量了。

3.引用只能引用变量,不能引用常量和表达式。

示例2:

double a=4;b=5
double & r1=a;//引用必须初始化,不可以更改其指向的目标
double & r2=r1;//r2也引用a
r2 = 10;
cout<<a<<endl;// >>10
r1 = b;//这里只是把b的值赋给r1也就是a,
//而并不是使引用的目标由对a的引用到对b的引用 
cout<<a<<endl;//>>5

引用的具体使用场景

  1. 引用型参数

使用引用型参数有两个好处:(1)因为函数形参和实参是同一个对象,也就不存在对象复制,避免了对象的开销。(2)可以在修改形参的同时对实参的修改。当然了,为了避免函数对原来实参的意外修改我们可以 用const 对引用加以修饰 也就是传常引用。传常引用有两个优势(1)因为不存在拷贝构造所以,可以提高c++程序的执行效率(2)避免对象切割问题。

  1. 引用型返回值

从函数中返回引用,一定要保证在函数返回以后,被引用的目标一直有效,也就是不能返回函数体内的局部对象的引用,大家都知道, 局部对象离开作用域就会被析构掉,所以不能返回对函数体内的局部对象的引用。

示例3:

void swap(int &a, int &b)
{

    int tmp;
    tmp=a;a=b;b=tmp;
}
int n1,n2;
swap(n1,n2);//交换a,b的值

示例4:

int n=4;
int &SetValue() {return n;}
int main()
{
    SetValue()=40;
    count<<n;//print 40
    return 0;
}

常引用

定义引用时,前面加const关键字,即为“常引用”

int n;
const int  & r=n;

r的类型是 const int&;

特性:不能通过常引用去修改其引用的内容

示例5:

int n =100;

const int & r=n;
r=200;//编译时出错
n=300;//ok

cont T & 和 T &是不同的类型。

T &类型的引用或T类型的变量可以用来初始化 const T &类型的引用。

const T类型的常变量和const T &类型的引用则不能用来初始化T &类型的引用,除非进行强制类型转换。

const关键字的用法

  1. 定义常量
    const int MAX_LEN=23;
  1. 定义常量指针

不可以通过常量指针修改其指向的内容

int n,m;
const int *p=&n;
*p=5;//编译时出错
n=4;//ok
p=&m; //ok,指针方向可以变化

不能把常量指针赋值给非常量指针,反之可行

const int *p1;int *p2;
p1=p2; //ok
p2=p1;//编译时出错
p2=(int *) p1; //强制类型转换后ok

函数参数为常量指针时,可避免函数内部不小心改变参数指针所指地方的内容。

  1. 不能通过常引用修改其引用的变量

2. 动态内存分配

分配变量:

    P= new T;

T是任意类型名,P是类型为T *的指针。
动态分配出一片大小为sizeof(T)字节的内存空间,并且将该内存空间的起始地址赋值给P。比如

int* pn;
pn= new int;
* pn= 5;

分配数组:

   P = new T[N];

T :任意类型名

P :类型为T *的指针

N :要分配的数组元素的个数,可以是整型表达式

动态分配出一片大小为sizeof(T)字节的内存空间,并且将该内存空间的起始地址赋值给P。
动态分配数组示例:

int* pn;
inti= 5;
pn= new int[i* 20];

用“new”动态分配的内存空间,一定要用“delete”运算符进行释放

delete 指针;//该指针必须指向new出来的空间

3. 内联函数 函数重载 函数缺省参数

内联函数

函数调用是有时间开销的。如果函数本身只有几条语句,执行非常快,而且函数被反复执行很多次,相比之下调用函数所产生的这个开销就会显得比较大。

为了减少函数调用的开销,引入了内联函数机制。编译器处理对内联函数的调用语句时,是将整个函数的代码插入到调用语句处,而不会产生调用函数的语句。

inline intMax(inta,intb)
{
if( a > b) return a;
return b;
}

函数重载

一个或多个函数,名字相同,然而参数个数或参数类型不相同,这叫做函数的重载

  • 以下三个函数是重载关系:
intMax(double f1,double f2) { }

intMax(intn1,int n2) { }

intMax(intn1,int n2,int n3) { }
  • 函数重载使得函数命名变得简单。
  • 编译器根据调用语句的中的实参的个数和类型判断应该调用哪个函数。

缺省函数

C++中,定义函数的时候可以让最右边的连续若干个参数有缺省值,那么调用函数的时候,若相应位置不写参数,参数就是缺省值。

void func( intx1, intx2 = 2, intx3= 3) { }
func(10 ) ; //等效于func(10,2,3)
func(10,8) ; //等效于func(10,8,3)

4.类

class CRectangle {
    public:
    int w, h;
    void Init( int w_, int h_ ) {
        w = w_; h = h_;
    }
    int Area() {
        return w * h;
    }
    int Perimeter() {
        return 2 * ( w + h );
    }
}; //必须有分号
int main() {
    int w, h;
    CRectangle r; //r是一个对象
    cin >> w >> h;
    r.Init(w, h);
    cout << r.Area() << endl << r. Perimeter();
    return 0;   
}

类定义的变量->类的实例->“对象”

对象的内存分配

对象的内存空间

•对象的大小 = 所有成员变量的大小之和

•E.g. CRectangle类的对象, sizeof(CRectangle) = 8

每个对象各有自己的存储空间

•一个对象的某个成员变量被改变, 不会影响到其他的对象

访问类的成员变量和成员函数

用法1: 对象名.成员名

    CRectangle r1, r2;
    r1.w = 5;
    r2.Init(3,4);

用法2: 指针->成员名

CRectangle r1, r2;
CRectangle * p1 = & r1;
CRectangle * p2 = & r2;
p1->w = 5;
p2->Init(3,4); //Init作用在p2指向的对象上

用法3: 引用名.成员名

CRectangle r2;
CRectangle & rr = r2;
rr.w = 5;
rr.Init(3,4); //rr的值变了,r2的值也变

类的成员函数的另一种写法

int CRectangle::Area() {
return w * h;
}
int CRectangle::Perimeter() {
return 2 * ( w + h );
}
void CRectangle::Init( int w_, int h_ ) {
w = w_; h = h_;
}