类
class Clock{
public:
void setTime(int newH,int newM,int newS);
private:
int hour,minute,second;
public:
void showTime();
};
注意:定义完类,最后要加分号
对象
Clock myClock;
myClock.showTime()
成员函数的实现
函数的原型声明要写在类体中,而具体实现是写在类定义之外的。
具体形式如下:
返回值类型 类名 ::函数成员名(参数表) 【请一定记住,因为实践编程时总是忘记】
{
函数体
}
void Clock::setTime(int newH,int newM,int newS){
hour=newH;
minute-newH;
second=newS;
}
void Clock::showTime(){
cout<<hour<<":"<<minute<<":"<<second<<endl;
}
带默认形参值的成员函数
类成员函数的默认值,一定要写在类定义中,而不能写在类定义之外的函数实现中。
class Clock{
public:
void setTime(int newH=0,int newM=0,int newS=0)
}
内联成员函数
隐式声明:
class Clock{
public:
void showTime(){
cout<<hour<<":"<<minute<<":"<<second<<endl;
}
}
显式声明(类定义中不加入showTime函数体):
inline void Clock::showTime(){
cout<<hour<<":"<<minute;
}
构造函数
构造函数的作用就是在对象被创建时利用特定的值构造对象,将对象初始化为一个特定的状态(简而言之——初始化对象的函数)
如果类中没有写构造函数,编译器会自动生成一个隐含的默认构造函数(无参数,无函数体)【注意:这不是带有默认形参值的构造函数,还望加以区分】
例:
class Clock{
public:
Clock (int newH,int newM,int newS); //构造函数
Clock (){
hour=0;
minute=0;
second=0;
} //习惯上会另设一个不带形参的构造函数
private:
int hour,minute,second;
};
Clock::Clock(int newH,int newM,int newS){
hour=newH;
minute=newM;
second=newS;
}
int main(){
Clock c(0,0,0);
}
构造函数的规则:
类名::类名(形参表) 【请一定记住,因为实践编程总是忘】
{
函数体
}
定义了构造函数(复制构造函数),编译系统不会再为其生成隐含的默认构造函数(隐含的复制构造函数)
可以重载,可以使用内联函数
复制构造函数
作用是使用一个已经存在的对象,去初始化同类的一个新对象(类似同类对象之间的赋值,只不过必须在初始化时使用)
例:
class Point{
public:
Point(int xx=0,int yy=0){
x=xx;
y=yy;
}
Point(Point &p); //复制构造函数的声明
private:
int x;
int y;
};
Point::Point(Point &p){
x=p.x;
y=p.y;
}
int main(){
Point a(1,2);
Point b(a); //复制构造函数
Point c=a; //复制构造函数
}
复制构造函数规则:
类名::类名(类名&) 【请一定记住,因为编程时总是忘记】
复制构造函数在以下3种情况会被调用:
1.当用类的一个对象去初始化该类的另一个对象时
2.如果函数的形参是类的对象,调用函数时,进行形参和实参结合时
(只有把对象用值传递时,才会调用复制构造函数,如果传递引用,则不会调用复制构造函数。由于这一原因,传递比较大的对象时,传递引用会比传值的效率高很多)
3.如果函数的返回值是类的对象,函数执行完成返回调用者时
简而言之——就是类的对象发生类似赋值的行为时就会调用复制构造函数
析构函数
析构函数是在对象的生存期即将结束的时刻被自动调用的,例如,对象如果在生存期间用 new 运算符动态分配了内存,则在各处写 delete 语句以确保程序的每条执行路径都能释放这片内存是比较麻烦的事情。有了析构函数,只要在析构函数中调用 delete 语句,就能确保对象运行中用 new 运算符分配的空间在对象消亡时被释放
析构函数不接收任何参数,名称是类名前加~
class String{
private:
char* p;
public:
String(int n);
~String(); //析构函数声明
};
String::~String(){
delete[] p; //析构函数实现
}
String::String(int n){
p = new char[n];
}
析构函数的调用顺序与构造函数刚好相反
#include <iostream>
using namespace std;
class CDemo {
public:
~CDemo() { cout << "destructor" << endl; }
};
void Func(CDemo obj) {
cout << "func" << endl;
}
CDemo d1;
CDemo Test() {
cout << "test" << endl;
return d1;
}
int main() {
CDemo d2;
Func(d2);
Test();
cout << "after test" << endl;
return 0;
}
程序共输出 destructor 四次:
- 第一次是由于 Func 函数结束时,参数对象 obj 消亡导致的。
- 第二次是因为:第 20 行调用 Test 函数,Test 函数的返回值是一个临时对象,该临时对象在函数调用所在的语句结束时就消亡了,因此引发析构函数调用。
- 第三次是 main 函数结束时 d2 消亡导致的。
- 第四次是整个程序结束时全局对象 d1 消亡导致的。
类的组合
类的组合描述的就是一个类内嵌其他类的对象作为成员的情况,它们之间的关系是一种包含与被包含的关系
当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动创建
构造的次序
1.调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的定义中出现的次序。注意,内嵌对象在构造函数的初始化列表中出现的顺序与内嵌对象构造函数的调用顺序无关。
2.执行本类构造函数的函数体
注:
1.如果有些内嵌对象没有出现在构造函数的初始化列表中,那么会执行内嵌对象的隐含默认构造函数。如果组合类的对象是没有指定对象的初始值,则默认形式(无形参)的构造函数被调用,这时内嵌对象的默认形式构造函数也会被调用。
2.有些数据成员的初始化,必须在构造函数的初始化列表中进行。这些数据策划给你要包括两类:一类是那些没有默认构造函数的内嵌对象——因为这类对象初始化时必须提供参数,另一类是引用类型的数据成员——因为引用类型的数据成员,引用型变量必须在初始化时绑定引用的对象。还有一类是常数据成员。如果一个类中包含这3类成员时,就必须编写显式构造函数。
例:
class Point{
public:
Point(int xx=0,int yy=0){
x=xx;
y=yy;
}
Point(Point &p);
int getX() {return x;}
int getY() {return y;}
private:
int x,y;
};
Point::Point(Point &p){
x=p.x;
y=p.y;
}
class Line{
public:
Line(Point xp1,Point xp2);
Line(Line &p1);
private:
Point p1,p2;
};
Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2){ //初始化列表
函数体
} //组合类的构造函数
Line::Line(Line &l):p1(l.p1),p2(l.p2){
函数体
}
int main(){
Point myp1(1,1),myp2(4,5);
Line line(myp1,myp2);
Line line2(line);
}
必须用初始化列表的情况:
1.类成员为const
2.类成员为引用
3.没有默认构造函数的类
4.如果类存在继承关系,派生类必须在其初始化列表中调用基类的构造函数
class Base
{
public:
Base(int a) : val(a) {}
private:
int val;
};
class A : public Base
{
public:
A(int v) : p(v), Base(v) {}
void print_val() { cout << "class A:" << p << endl;}
private:
int p;
};
int main()
{
int m = 45;
A b(m);
b.print_val();
}
前向引用声明
class B; //前向引用声明
class A{
public:
void f(B b);
};
class B{
public:
void g(A a);
};
尽管使用了前向引用声明,但是在提供一个完整的类定义之前,不能定义该类的对象,也不能在内联成员函数中该用该类的对象
但是可以声明类的对象引用或指针,函数形参和返回值
UML图
#表示protected
结构体
结构体和类的唯一区别在于:对于未指定访问控制属性的成员,类(private),结构体(public)
在c++中已经没有什么用了(书上的意思)