2章 C++.txt

struct point
{
int x;
int y;
void output()
{
cout<<x<<endl<<y<<endl;
}


}
在C语言中,结构体中是不能有函数的,然而在c++中,结构体是可以有函数的,称为成员函数。
定义类和结构体时,不要忘了右花括号处加上(;)。
#include<iostream>
using namespace std;


class point
{
public:
int x;
int y;
/*
void init()
{
x=5;
y=9;
}
*/
point() //构造函数名字于类名相同,没有返回值。
{
x=5;
y=9;
}
void output()
{
cout<<x<<endl<<y<<endl;
}
};
void main()
{
point pt;
/*
pt.init();
pt.output();
*/
pt.output();


}
构造函数的作用是对对象本身做初始化工作,也就是给用户提供初始化类中成员变量的一种方式。
如果一个类中没有定义任何的构造函数,那么C++编译器在某些情况下会为该类提供一个默认的构造函数,这个默认的构造函数是一个不带参数的构造函数。只要一个类中定义了一个构造函数,不管这个构造函数是否是带参数的构造函数,C++编译器就不在提供默认的构造函数。也就是说,如果为一个类定义了一个带参数的构造函数,还想要无参数的构造函数,则必须自己定义。
在三种情况下,会提供默认构造函数:
1.如果类有虚拟成员函数或者虚拟继承父类(即有虚拟基类)时;
2.如果类的基类有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数);
3.在类中的所有非静态的对象数据成员,它们所属的类中有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数)。
析构函数不允许有返回值,更重要的是析构函数不允许带参数,并且一个类中只能有一个析构函数。
对一个对象来说,析构函数是最后一个被调用的成员函数。
class Student
{
private:
char* pName;
public:
Student()
{
pName=new char[20];
}
~Student()
{
delete[] pName;
}
};
在类中定义成员变量时,不能直接给成员变量赋初值。例如:
class point
{
int x=0;//错误,此处不能给变量x赋值
int y;
};
重载:
#include<iostream>
class point
{
public:
int x;
int y;
point()
{
x=0;
y=0;
}
point(int a,int b)
{
x=a;
y=b;
}
void output()
{
cout<<x<<endl<<y<<endl;
}
};
void main()
{
point pt(5,5);
pt.output();
}
这个程序中,有两个构造函数,他们的函数名是一样的,只是参数的类型和个数不一样。这在C语言中是不允许的,而在C++中是合法的,这就是C++中函数的重载(overload)
两个例子:
1. (1)void output();//要注意:只有函数的返回类型不同时不能构成函数的重载的。
   (2)int output();
2. (1)void output(int a,int b=5);//output(5)该调哪一个,在函数重载时,要注意函数带有默认参数的这种情况。
   (2)void output(int a);
this:
#include<iostream.h>
class point
{
public:
int x;
int y;
point()
{
x=0;
y=0;
}
point(int a,int b)
{
x=a;
y=b;
}
void output()
{
cout<<x<<endl<<y<<endl;
}
void input(int x,int y)
{
x=x; //this->x=x;
y=y; //this->y=y;
}
};
void main()
{
point pt(5,5);
pt.input(10,10);
pt.output();
}
结果不是10 10 而是 5 5。读者可能会认为在input(int x,int y)函数中,利用形参x和形参y对point类中的成员变量x和y进行了赋值,然而事实是这样吗?因为变量的可见性,point类的成员变量x和y在input(int x,int y)这个函数中是不可见的,所以,我们实际上时将形参x的值赋给了形参x,将形参y的值赋给了形参y,根本没有给point类的成员变量x和y进行赋值,程序运行的结果当然就是"5,5"了。
如何在input(int x,int y)这个函数中对point类的成员变量x和y进行赋值呢?有的读者马上就想到,将input函数的参数名改一下不就可以了吗?比如:将函数改为input(int a,int b),当然这也是一种解决办法。如果我们不想改变函数的参数名,那么又如何给point类的成员变量x和y进行赋值呢?
在这种情况下,可以利用C++提供的一个特殊的指针—this。来完成这个工作。this指针是一个隐含的指针,它是指向对象本身的,代表了对象的地址。一个类所有的对象调用的成员函数都是同一个代码段,那么,成员函数又是怎么识别属于不同对象的数据成员呢?原来,在对象调用pt.input(10,10)时,成员函数出了接收2个实参外,还接收到了pt对象的地址,这个地址被一个隐含的形参this指针所获取,它等同于执行this=&pt。所有对数据成员的访问都隐含地被加上了前缀this->。例如:x=0;等价于this->x=0。
在C++中,提供了一种重要的机制,就是继承。(基类,Base Class父类)(派生类,Derived Class,子类)
类的继承:
在子类被调用之前,父类先被调用(没有父亲就没有孩子),析构是正好相反。#include<iostream.h>


class animal
{
public:
animal(int height,int weight)
{
cout<<"animal construct"<<endl;
}
~animal()
{
cout<<"animal destruct"<<endl;
}
void eat()
{
cout<<"animal eat"<<endl;
}
void sleep()
{
cout<<"animal sleep"<<endl;
}
void breathe()
{
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
public:
fish() //fish():animal(500,400)
{
cout<<"fish construct"<<endl;
}
~fish()
{
cout<<"fish destruct"<<endl;
}
};
void main()
{
fish fh;
}
当我们构建fish类的对象fh时,它需要先构造animal类的对象,调用animal类的默认构造函数(即不带参数的构造函数),而在我们的过程中,animal类只有一个带参数的构造函数,在编译时,因找不到animal类的默认构造函数而出错.
因此,在构造fish类的对象时(调用fish类的构造函数时),要想办法去调用animal类的带参数的构造函数,也可以用上面//显式地调用父类的带参数的构造函数。
访问权限:
public定义的成员可以在任何地方被访问.
protected定义的成员只能在该类及子类中访问.
private定义的成员只能在该类自身中访问。
多重继承:
class 派生类名:访问权限 基类名称,访问权限 基类名称
{


};
一般情况,多重继承的位置无影响.有些有影响:
1.由构造函数引起的初始化发生的顺序。
2.激活析构函数以做清除工作的顺序。
#include<iostream.h>


class B1
{
public:
void output();
};
class B2
{
public:
void output();
};
void B1::output()
{
cout<<"call the class B1"<<endl;
}
void B2::output()
{
cout<<"call the class B2"<<endl;
}
class A:public B1,public B2
{
public:
void show();
};
void A::show()
{
cout<<"call the class A"<<endl;
}
void main()
{
A a;
a.output(); //编译器无法确定用户到底需要是哪一个基类的output()
a.show();
}
#include<iostream.h>


class animal
{
public:
void eat()
{
cout<<"animal eat"<<endl;
}
void sleep()
{
cout<<"animal sleep"<<endl;
}
void breathe() //virtual void breathe() 
{
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
public:
void breathe()
{
cout<<"fish bubble"<<endl;
}
};
void fn(animal *pAn)
{
pAn->breathe();
}
void main()
{
animal *pAn;
fish fh;
pAn=&fh;
fn(pAn);
}
在虚函数是 用到迟绑定(late binding)技术.
C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数.如果对象类型是派生类,就调用派生类的函数;如果对象类是基类,就调用基类的函数。
纯虚函数:是指表明为不具体实现的虚成员函数(纯虚函数也可以有函数体,但这种提供函数体的用法很少见)。纯虚函数可以让类西安具有一个操作名称,而没有操作内容,让派生类在继承时再去具体给出定义。凡是含有纯虚函数的类叫做抽象类。这种类不能声明对象,只是作为基类为派生类服务。在派生类中必须完全实现基类的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。
??C++的多态性是由虚函数来实现的,而不是纯虚函数。在子类中如果有对基类虚函数的覆盖定义,无论该覆盖定义是否有virtual关键字,都是虚函数。
函数的覆盖和隐藏
函数的覆盖:
class animal
{
public:
virtual void breathe()
{
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
public:
void breathe()
{
cout<<"fish bubble"<<endl;
}
};
在基类animal的breathe函数前添加了virtual关键字,声明该函数为虚函数.在派生类fish中重写了breathe函数,fish的breathe函数和animal的完全一样,无论函数名还是参数列表都是一样,这称为函数的覆盖(override)。
构成函数覆盖的条件为:
1.基类函数必须是虚函数(使用virtual关键字进行声明)。
2.发生覆盖的两个函数要分别位于派生类和基类中。
3.函数名称与参数列表必须完全相同。
class animal
{
public:
void breathe()
{
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
public:
void breathe()
{
cout<<"fish bubble"<<endl;
}
};
breathe不是虚函数,隐藏.
隐藏:指派生类中具有与基类同名的函数(不考虑参数列表是否相同),从而在派生类中隐藏基类的同名函数。
函数隐藏、覆盖、重载:
1.派生类的函数与基类的函数完全相同(函数名和参数列表都相同),只是基类的函数没有使用virtual关键字。此时基类的函数将被隐藏,而不是覆盖。
2.派生类的函数与基类的函数同名,但参数列表不同,这种情况下,不管基类的函数声明是否有virtual关键字,基类的函数都将被隐藏。
class Base
{
public:
virtual void fn();
};
class Derived: public Base
{
public:
void fn(int);   //隐藏
};
class Derived2 :public Derived//注意看是继承谁
{
public:
void fn();   //隐藏
};
如何更好的区分覆盖和隐藏呢? 实际上只要记住一点:函数的覆盖是发生在派生类与基类之间,两个函数必须完全相同,并且都是虚函数。那么不属于这种情况的,就是隐藏了.
#include<iostream.h>
class Base
{
public:
virtual void xfn(int i)
{
cout<<"Base::xfn(int i)"<<endl;
}
void yfn(float f)
{
cout<<"Base::yfn(float f)"<<endl;
}


void zfn()
{
cout<<"Base::zfn()"<<endl;
}
};
class Derived:public Base
{
public:
void xfn(int i) //覆盖了基类的xfn函数
{
cout<<"Drived::xfn(int i)"<<endl;
}
void yfn(int c)//隐藏了基类的yfn函数
{
cout<<"Drived::yfn(int c)"<<endl;
}
void zfn() //隐藏了基类的zfn函数
{
cout<<"Drived::zfn()"<<endl;
}
};
void main()
{
Derived d;


Base *pB=&d;
Derived *pD=&d;

pB->xfn(5);
pD->xfn(5);


pB->yfn(3.14f);
pD->yfn(3.14f);


pB->zfn();
pD->zfn();
}
自己的结果: 正确的答案:
Base::xfn(int i) Drived::xfn(int i)
Drived::xfn(int i) Drived::xfn(int i)
Base::yfn(float f) Base::yfn(float f)
Drived::yfn(int c) Drived::yfn(int c)
Base::zfn() Base::zfn()
Drived::zfn() Drived::zfn()
自己总结:覆盖,记住就像电脑里的操作覆盖一样,被覆盖掉的就没了,消失了。
引用:就是一个变量的别名。
引用和指针变量区别:引用只是一个别名,是一个变量或对象的替换名称。引用的地址就是它所引用的变量或者对象的地址,对引用的地址所做的操作就是对被引用的变量或对象的地址所做的操作。指针是地址,指针变量要存储地址值,因此要占用存储空间,我们可以随时修改指针变量所保存的地址值,从而指向其他的内存。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值