之前C++学得就不太扎实,正好看到《VC++深入详解》第二章对C++进行了一个简单总结,故整理回顾之。
例如下面的程序就会报错,因为x和y都是默认的私有成员,只有在类的内部才能进行访问
#include
using namespace std;
class point
{
int x;
int y;
void output()
{
cout << x << endl << y << endl;
}
};
int main(void)
{
point pt;
pt.x = 0; //无法访问
pt.y = 0; //无法访问
pt.output(); //无法访问
return 0;
}
为了解决上面的问题,使得可以对类内的成员进行初始化,我们用构造函数进行。先来看没有构造函数直接进行输出是x,y的值:
#include
using namespace std;
class point
{
public:
int x;
int y;
void output()
{
cout << x << endl << y << endl;
}
};
int main(void)
{
point pt;
pt.output();
return 0;
}
在没有进行初始化的条件下,输出为x=-858993460,y=-858993460(或者其它毫不相关的数)。
下面引入构造函数后:
#include
using namespace std;
class point
{
public:
int x;
int y;
point() //point的构造函数
{
x = 0;
y = 0;
}
void output()
{
cout << x << endl << y << endl;
}
};
int main(void)
{
point pt;
pt.output();
return 0;
}
class point
{
int x = 0;//错误,此处不能给变量x赋值
int y;
};
下面先看一个代码:
#include
using namespace std;
class point
{
public:
int x;
int y;
point() //point的第一个构造函数
{
x = 0;
y = 0;
}
point(int a, int b) //point的第二个构造函数
{
x = a;
y = b;
}
void output()
{
cout << x << endl << y << endl;
}
};
int main(void)
{
point pt(6, 8);
pt.output();
return 0;
}
例如下面这段代码:
class Student
{
private:
char *pName;
public:
Student()
{
pName = new char[20];
}
~Student()
{
delete[] pName; //如果类中没有用到指针,则析构函数内部空着就好
}
};
先来看几个例子:
#include
using namespace std;
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;
y = y;
}
};
int main(void)
{
point pt(5, 5);
pt.input(10, 10);
pt.output();
return 0;
}
输出为:
5
5
那么为什么输出不是10, 10呢,因为在input函数中,point类的成员变量x和y都是不可见的。并不是说成员变量在成员函数中不可见,而是如果成员函数中定义了和成员变量相同的变量,则成员变量在该成员函数中不可见,在下一个例子中可以说明这一点。
#include
using namespace std;
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)
{
y = x;
}
};
int main(void)
{
point pt(5, 5);
pt.input(10);
pt.output();
return 0;
}
输出为:
5
10
在这个例子中,从输出x=5,y=10可以看出:成员变量y在成员函数input中就是可见的, 成员变量x在成员函数input中是不可见的。
那么该如何利用成员函数给成员变量赋值呢,可以用下面两个方法:
#include
using namespace std;
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 c, int d)
{
x = c;
y = d;
}
};
int main(void)
{
point pt(5, 5);
pt.input(10, 10);
pt.output();
return 0;
}
输出为:
10
10
所以我们可以这样写这个程序:
#include
using namespace std;
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)
{
this->x = x;
this->y = x;
}
};
int main(void)
{
point pt(5, 5);
pt.input(10, 10);
return 0;
}
输出为:
10
10
先看下面这个例子:
#include
using namespace std;
class animal
{
public:
void eat()
{
cout << "animal eat" << endl;
}
void sleep()
{
cout << "animal sleep" << endl;
}
void breathe()
{
cout << "animal breathe" << endl;
}
};
class fish:public animal
{
};
int main(void)
{
animal an;
fish fh;
an.eat();
fh.eat();
return 0;
}
接下来看一下子类和父类的构造函数和析构函数的顺序:
#include
using namespace std;
class animal
{
public:
animal()
{
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()
{
cout << "fish construct" << endl;
}
~fish()
{
cout << "fish destruct" << endl;
}
};
int main(void)
{
fish fh;
return 0;
}
输出为:
animal construct
fish construct
fish destruct
animal destruct
可以看出,在声明子类对象时,父类的构造函数先运行,然后子类的构造函数,在对象声明周期结束时,子类的析构函数先运行,然后是父类的析构函数。
#include
using namespace std;
class animal
{
public:
animal(int height, int weight)
{
cout << "animal construct" << endl;
}
};
class fish:pubilc animal
{
pubilc:
fish():animal(400, 300)
{
cout << "fish construct" << endl;
}
};
int main(void)
{
fish fh;
return 0;
}
在fish类的构造函数后,加一个冒号(:),然后加上父类的带参数的构造函数。这样,在子类的构造函数被调用时,系统就会调用父类的带参数的构造函数去构造对象。
如同该名字中所描述的,一个类可以从多个基类中派生。
定义形式为:
class 派生类名 : 访问权限 基类名称, 访问权限 基类名称
{
……
};
例如B类是由类C和类D派生的,可按如下方式进行说明:
class B : public C, public D
{
……
};
C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象是派生类,就调用派生类的函数;如果对象是基类,就调用基类的函数。C++的多态性是由虚函数类实现的,而不是纯虚函数。
下面通过两个例子来对比说明:
#include
using namespace std;
class animal
{
public:
void eat()
{
cout << "animal eat" << endl;
}
void sleep()
{
cout << "animal sleep" << endl;
}
void breathe()
{
cout << "animal breathe" << endl;
}
};
class fish:public animal
{
public:
void breathe()
{
cout << "fish bubble" << endl;
}
};
void fn(animal *pAn)
{
pAn->breathe();
}
int main(void)
{
animal *pAn;
fish fh;
pAn = &fh;
fn(pAn);
return 0;
}
输出为:
animal breathe
#include
using namespace std;
class animal
{
public:
void eat()
{
cout << "animal eat" << endl;
}
void sleep()
{
cout << "animal sleep" << endl;
}
virtual void breathe()
{
cout << "animal breathe" << endl;
}
};
class fish:public animal
{
public:
void breathe()
{
cout << "fish bubble" << endl;
}
};
void fn(animal *pAn)
{
pAn->breathe();
}
int main(void)
{
animal *pAn;
fish fh;
pAn = &fh;
fn(pAn);
return 0;
}
输出为:
fish bubble
为什么例一的输出结果为“animal breathe”而不是“fish bubble”呢?
用virtual关键字申明的函数叫做虚函数。
class animal
{
public:
void eat()
{
cout << "animal eat" << endl;
}
void sleep()
{
cout << "animal sleep" << endl;
}
virtual void breathe() = 0;
};
构成函数覆盖的条件:
例:
class animal
{
public:
...
virtual void breathe()
{
cout << "animal breathe" << endl;
}
...
};
class fish:public animal
{
public:
...
void breathe()
{
cout << "fish bubble" << endl;
}
...
};
上述例子中,fish类中的breathe()函数就实现了对animal类中breathe函数的覆盖。fish类中的breathe()函数仍然是虚函数。
class animal
{
public:
...
void breathe()
{
cout << "animal breathe" << endl;
}
...
};
class fish:public animal
{
public:
...
void breathe()
{
cout << "fish bubble" << endl;
}
...
};
两种函数隐藏的情况:
例二
class Base
{
public:
virtual void fn();
};
class Derived:public Base
{
public:
void fn(int);
};
class Derived2:public Derived
{
public:
void fn();
};
引用就是一个变量的别名。它需要用另一个变量或对象来初始化自身。引用就像一个人的外号一样。
//下面的代码生命了一个引用b,并用变量a进行了初始化
int a = 5;
int &b = a;
用&表示申明一个引用,引用必须在申明时进行初始化。
int a = 5;
int& b = a ;
int c = 3;
b = c;
上例中,并不是将b变成c的引用,而是给b赋值,此时b和a的值都变成了3。
#include
using namespace std;
//change函数主要用来交换a和b的值
void change(int& a, int& b);
int main(void)
{
int x = 5;
int y = 3;
cout << "original x = " << x << endl;
cout << "original y = " << y << endl;
change(x, y); //此处如果用指针传递,则调用change(&x, &y),这样很容易让人迷惑,不知道交换的是x和y的值,还是x和y的地址?此处使用引用,可读性就比指针要好
cout << "changed x = " << x << endl;
cout << "changed y = " << y << endl;
return 0;
}
/*
change()函数中采用了一个巧妙的算法来实现了a和b值的互换
*/
void change(int& a, int& b)
{
a = a+b;
b = a-b;
a = a-b;
}
上述例子中,不能将函数定义成void change(int a, int b)。 如果定义成这样,在调用完成之后,x还是等于原来的x,y还是等于原来的y,因为函数中的a=x,b=y,而x!=a,y!=b。使用引用就不同了,使用引用后,a和x,b和y事实上是相同的,因为他们在内存中占用的是同一个内存单元。
在设计一个类的时候,通常是将类的定义及类成员函数的声明放到头文件(即.h文件)中,将类中成员函数的实现放到源文件(即.cpp)中。对于main()函数,我们则单独把它放到main.cpp文件中。
对于头文件重复包含的情况,如main.cpp包含了animal.h文件和fish.h文件,而fish.h文件又包含了animal.h文件。这样就会出现头文件重复包含了,编译报错:‘class’ type redefinition。解决方法如下,在每一个头文件中,都使用条件预处理指令,如下:
#ifndef _ANIMAL_H_
#define _ANIMAL_H_
class animal
{
public:
animal();
~animal();
void eat();
void sleep();
virtual void breathe();
};
#endif