一 类的声明和实现
1. class tdate //声明部分
{
public:
void setdate(int y,int m,int d);
int isleapyear();
void print();
private:
int year,month,day;
}
void tdate::setdate(int y,int m,int d)//外联函数
{
year=y;
month=m;
day=d;
}
int tdate::isleapyear()
{
return(year%4==0&&y ear%100!=0)||(year%400==0);
}
void tdate::print()
{
cout<<year<<"."<<month<<"."<<day<<endl;
}
::为作用域运算符 标识某个成员函数是属于哪个类的
声明方式二
class tdate
{
public:
void setdate(int y,int m,int d) //内联函数 适合函数体比较小
{
year=y;
month=m;
day=d;
}
int isleapyear(){....}
private:
int year,month,day;
}//内联函数的声明方法 将成员函数的实现 都写在类体内 ;如果函数的实现定义在类体外,则需要在函数头前面加上该函数所属的类的标识,使用作用域运算符 ::
2: 注意事项
1.类体中不允许对所定义的数据成员进行初始化 需要使用构造函数
2.类中的数据成员的类型可以是任意的,其它类的对象可以作为该类的成员,但是自身类的对象不可以,但是自身类的指针或者引用可以。
另一个类的对象作为这个类的成员时,如果另一个类的定义在后,则需要提前说明,这种说明成为引用性说明。
class N;
class M
{
public: ...
private:
N n;//n是N类的对象 N类在前面说明
}
class N
{
public:
void f(M m); //m是M类的对象 M类已在前面说明
...
};
M类中使用了N类的对象n,而N类的定义又在后面,所以,对N类要提前说明
3.先声明public 公有成员,后声明私有private 私有成员 一般按照数据成员的类型大小 由小到大来说明
4.将类定义的说明部分或整个实现部分放到一个头文件中 ;也可以将声明放在一个头文件,实现放在另一个文件中
二.对象的定义
类名 对象名
tdate t1,t2,t3,* pdate,date[31],&rdate=t1; //其中多个对象用逗号隔开 pdate为指向tdate对象的指针,date为一个对象数组, rdate 为一个对象的引用。
对象成员的表示方法
对象名.成员名
对象的指针名->成员名
对象名.成员名(参数表)
对象的指针名->成员名(参数表)
(* 指针).成员名
同一个类所创建的对象的数据结构是相同的,类中的成员函数是共享的。
三.对象的初始化
1.构造函数和析构函数
都是特殊的成员函数:构造函数初始化正在创建的对象;析构函数的功能是用来释放一个对象
class tdate//类声明
{
public:
tdate(int y,int m,int d); //构造函数 非默认
~tdate(); //析构函数 非默认
void print(); //普通的成员函数
private: //数据成员变量
int year,month,day;
}
tdate::tdate(int y,int m,int d) //构造函数的实现
{
year=y;
month=m;
day=d;
cout<<"constructor called.\n";
}
tdate::~tdate() //析构函数的实现
{
cout<<"destructor called.\n";
}
void tdate::print() //普通成员函数的实现
{
cout<<year<<"."<<month<<"."<<day<<endl;
}
构造函数特点:
a 构造函数可以写在类体内(内联),也可以写在类体外 (外联)
b 构造函数为特殊的成员函数 名称同类名,不指定类型,有隐含的返回值,此值由系统内部使用。该函数可以没有参数,也可以有多个参数
c 构造函数可以重载,根据参数个数不同
d 程序不直接调用构造函数,创建对象时,系统自动调用构造函数
析构函数特点:
a 同构造函数
b 类名前加~ 用来与构造函数加以区别 不指定数据类型,无参数
c 只能有一个析构函数 这一点不同于构造函数
d 通常系统自动调用 下面的两种情况
1.函数体内定义的对象 当函数结束时,系统将调用析构函数释放此对象
2.一个对象使用new运算符被动态创建时,则使用delete运算符(需要手动)释放,delete将自动调用析构函数
源代码如下:
#include <iostream.h>
#include "tdate.h"
void main()
{
tdate today(1998,4,9),tomorrow(1998,4,10);
cout<<"today is";
today.print();
cout<<"tomorrow is";
tomorrow.print();
}
执行结果如下:
constructor called.
constructor called.
today is 1998.4.9
tomorrow is 1998.4.10
destructor called.
destructor called.
2.默认构造函数和默认析构函数
默认构造函数: 无参的构造函数 称为默认构造函数
a 系统自动提供的 程序员未定义 则系统自动生成
b 程序员定义的
c 系统默认构造函数 创建对象,外部类对象和静态类对象的所有数据成员默认值,自动类对象的所有数据成员为无意义值。
默认的析构函数:系统提供 的一个空函数
程序员定义的析构函数可以不是一个空函数,可以给出适当的函数体。
3.拷贝/复制构造函数
用一个已知对象来创造一个新的对象,而新创建的对象与已知对象的数据成员的值可以相同,也可以不同。
仅有一个参数,就是已知对象的引用
类名::复制构造函数名(类名 & 引用名)
{
函数体
}
如果程序员未定义,则系统提供默认的复制构造函数 该类的公有成员
案例
class tpoint
{
public:
tpoint(int x,int y){X=x;Y=y;}
tpoint(tpoint & p);
private:
int X,Y;
}
tpoint::tpoint(tpoint & p)
{
X=p.x;
Y=p.y;
cout<<"adsfalsdf.\n";
}
调用:
#include <iostream.h>
#include "tpoint.h"
void main()
{
tpoint p1(5,7);
tpoint p2(p1);
cout<<"asdfasdf"<<endl;
}
需要调用复制构造函数来由一个对象初始化另一个对象
a 明确表示一个对象初始化另一个对象
tpoint p2(p1);
b 当对象作为函数实参传递给函数形参时
p=f(N);
c 当一个自动存储类对象作为函数返回值时
return R;
案例代码:
...
tpoint f(tpoint q); //prototype
void main()
{
tpoint m(20,35),p(0,0); //构造函数初始化
tpoint n(m);//复制构造函数
p=f(n);//n对象作为参数传递给函数f
cout<<"adsf"<<"adsfasdf"<<endl;
}
tpoint f(tpoint q)
{
...
tpoint r(x,y);//调用构造函数初始化对象
return r;//返回一个tpoint类的对象
}
代码中标红的位置是复制构造函数被调用的位置。
1.tpoint n(m) n对象被复制构造函数初始化
2.f函数 实参n传递给形参q时 会调用复制构造函数
3.f函数定义的最后 return r 函数返回的对象由于是局部变量 类型为自动存储类变量 会复制到一个无名的此类的对象,然后再返回,给原函数,所以此处会调用复制构造函数
析构函数 根据构造函数来分析 共调用6次析构函数
1.退出f函数时,调用2次析构函数,用来释放对象r和q 一个是形参对象 另一个是返回的r对象
2.主函数中,将f函数的值赋值给对象p后,无名对象(被r赋值的对象)被析构,退出主函数时,调用3此析构函数 分别释放n p和m
4.成员函数的特性
a 内联函数和外联函数
内联为定义在类体内的成员函数 执行时不转移ip执行,而是在调用内联函数处,用内联函数来替换 以节省开销,提高速度 ,类似于宏
外联为说明在类体内,但实现在类体外的成员函数
b 外联变内联 在外联函数前加上inline
案例:
class A
{
public:
A(int x,int y){X=x;Y=y;} //内联函数
int a(){return X;} //内联函数
int b(){return Y;} //内联函数
int c();
int d();
private:
int X,Y;
};
inline int A::c() //内联函数
{
return a()+b();
}
inline int A::d() //内联函数
{
return c();
}
#include <iostream.h>
void main()
{
A m(3,5); //构造函数调用
int i=m.d(); //对象的成员函数d()再调用c() 再调用a和b
cout<<"d() return :"<<i<<endl;
}
重载性
一般的成员函数,特殊的构造函数可重载,但是析构函数是不能重载的 注意重载函数中的参数个数不同
案例:
class m
{
public:
m(int x,int y){X=x;Y=y;} //构造函数 1
m(int x){X=x;Y=x*x;} //构造函数2 重载
int add(int x,int y); //成员函数1
int add(int x); //成员函数2
int add(); //成员函数3 重载
int xout(){return X;}
int yout(){return Y;}
private:
int X,Y;
};
int m::add(int x,int y)
{
X=x;
Y=y;
return X+Y;
}
int m::add(int x)
{
X=Y=x;
return X+Y;
}
....
4.3设置参数的默认值
成员函数和构造函数都可以设置参数的默认值
案例:
class n
{
public:
n(int a=3,int b=5,int c=7);
int Aout(){return A;}
int Bout(){return B;}
int Cout(){return C;}
private:
int A,B,C;
};
n::n(int a,int b,int c) //构造函数 三个参数
{
A=a;
B=b;
C=c;
}
#include <iostream.h>
void main()
{
n x,y(9,11),z(13,15,17)
cout<<"x="<<x.Aout()<<","<<x.Bout()<<","<<x.Cout()<<endl;
cout<<"y="<<y.Aout()<<","<<y.Bout()<<","<<y.Cout()<<endl;
cout<<"z="<<z.Aout()<<","<<z.Bout()<<","<<z.Cout()<<endl;
}
程序执行输出如下:
X=3,5,7
Y=9,11,7
z=13,15,17
未提供实参的使用原类体内的给出的默认值,给实参的使用实参值
五 静态成员
全局变量或对象和局部的静态变量或对象能解决数据共享问题
案例1:
#include <iostream.h>
int g=5; //全局
void f1(),f2(); //函数原型
void main()
{
g=10; // 此时g是全局变量
f1(); //函数调用
f2(); //函数调用
cout<<g<<endl; //输出变量g
}
void f1()
{
g=15; //修改了全局变量
}
void f2()
{
g=20; //修改了全局变量
}
输出结果为20,函数间共享变量g, 全局变量或对象在任何位置都可以被修改。 不安全
1.静态成员包含静态数据成员和静态成员函数。它们都属于类,也属于类的所有对象。
2.静态成员可以用对象引用,也可以用类来引用。
3.静态数据成员和静态成员函数在没有对象时就已经存在,并可以用类来引用。
A 静态数据成员
实现多个对象之间的数据共享,并使用静态数据成员还不会破坏隐藏的原则,保证安全性。
静态数据成员是类的所有对象共享的成员,而不是某个对象的成员。
只存储一处,供所有对象公用。可以由共享它的任一个对象修改更新。
1.静态数据成员在定义或说明时前面加上关键字static
private:
int a,b,c;
static int s;
其中a,b和c是非静态数据成员,而s是静态数据成员
2.静态数据成员的初始化与一般数据成员不同
在类体外进行
数据类型 类名::静态数据成员名=初值
a 初始化在类体外进行,前面不加static,以免与一般静态变量或对象相混淆
b 作用域运算符来标明它所属的类,静态数据成员属于类,不属于某个对象
3.静态数据成员被存放在静态存储区
静态数据成员是被放在静态存储区的,必须进行初始化。
案例:
class nclass
{
...
private:
static int a; //静态成员变量 声明
...
};
int nclass::a=5; //必须初始化
...
4.引用静态数据成员的格式
类名::静态成员名
案例:
class myclass
{
public:
myclass(int a,int b,int c); //构造函数
..
private:
int A,B,C;
static int Sum; //静态成员变量声明
};
int myclass::sum=0; //初始化
myclass::myclass(int a,int b,int c) //构造函数的实现
{
A=a;
B=b;
C=c;
sum+=A+B+C;
}
void main()
{
myclass m(3,7,10),n(14,9,11);
cout<<m.A<<m.B<<m.C<<endl;
cout<<n.A<<n.B<<n.C<<endl;
cout<<m.sum<<endl;
cout<<n.sum<<endl;
}
执行结果为
3,7,10
14,9,11
54
54
将m初始化时 三个数的和赋值给了sum,值为20,之后n初始化又为34 sum的总值为54;可见静态数据成员是多个对象共享的。
B 静态成员函数
同静态成员变量一样,函数也是属于类,而不是具体的对象,所以使用时可以用类名来引用静态成员函数。
静态成员函数实现的函数体中可以直接引用类中说明的静态成员,而不可以直接引用类中说明的非静态成员。
静态成员函数中要引用非静态成员时,可通过对象来引用。
#include <iostream>
#include <math.h>
using namespace std;
class point
{
public:
point(double _x,double _y)
{
x=_x;y=_y;
}
void getxy();
friend double distance(point &a,point &b); //友元函数的声明
private:
double x,y;
};
void point::getxy()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
double distance(point &a,point &b) //友元函数的实现 注意参数和声明的方式
{
double dx=a.y-b.x;
double dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
int main()
{
point p1(3.0,4.0),p2(6.0,8.0);
p1.getxy();
p2.getxy();
double d=distance(p1,p2);
cout<<"distance is"<<d<<endl;
}
执行结果为:
(3.0,4.0) (6.0,8.0) distance is 5
案例2:
#include <iostream>
#include <math.h>
using namespace std; //命名空间
class time //类 声明
{
public:
time(int _h,int _m) //内联函数
{
h=_h;m=_m;
}
friend void time12(time t); //友元
friend void time24(time t);
private:
int h,m;
};
void time12(time t) //友元实现
{
if(t.h>12)
{
t.h-=12;
cout<<t.h<<":"<<t.m<<"PM"<<endl;
}
else
cout<<t.h<<":"<<t.m<<"AM"<<endl;
}
void time24(time t) //友元实现
{
cout<<t.h<<":"<<t.m<<endl;
}
int main() //主函数
{
time t1(20,30),t2(10,45); //调用构造 初始化对象
time12(t1); //友元调用
time24(t1);
time12(t2);
time24(t2);
return 0;
}
执行结果:
8:30 PM
20:30
10:45AM
10:45
如果形参改为引用 则函数内部的实现 修改的是实参对象的值
8:30 PM
8:30
10:45AM
10:45
友元类
一个类作为另一个类的友元,这个类的所有成员函数都是另一个类的友元函数,
class x
{
friend class y;
public:...
private:
int a;
...
}
class y
{
public:
void display();
private:
...
}
y类作为x类的友元类 y中的成员函数display 是x类的友元函数 可以访问x的私有成员变量
案例:
#include <iostream>
#include <math.h>
using namespace std;
class X
{
friend class Y; //声明友元类
public:
void set(int i)
{
x=i;
}
void display()
{
cout<<"x="<<x<<",";
cout<<"y="<<y<<endl;
}
private:
int x; //成员变量
static int y; //静态成员变量
};
class Y //类声明
{
public:
Y(int i,int j); //构造函数
void display();
private:
X a; //私有成员变量为另一个类的对象
};
int X::y=1; //静态成员变量的初始化
Y::Y(int i,int j) //Y类中的Y构造函数
{
a.x=i; //x类对象a的x成员变量设置为i 此时x成员变量为非静态成员
X::y=j; //X类中的私有变量设置为j 因为此为构造函数,也是成员函数 即构造函数也是X类的友元函数,可以访问私有的成员变量X::y
}
void Y::display() //Y类中的display函数的实现
{
cout<<"X="<<a.x<<","; // 访问X类对象a的x成员变量 对象名调用普通成员变量
cout<<"y="<<X::y<<endl; // 访问X类y成员变量 类名调用静态成员变量 共享
}
int main()
{
X b; //b对象
b.set(5); //构造初始化
b.display();
Y c(6,9); //Y类的c对象
c.display();
b.display();
return 0;
}
x=5;//b.display()
y=1;
x=6;//c.display()
y=9;
x=5;//b.display()
y=9;
注意:友元的关系不可逆 y类是x类的友元类,不能说x类是y类的友元类
5.7类的作用域
类的作用域 简称类域,类的定义中由一对花括号括起来的部分。
类的成员 位于类域中。
类域中的变量不能使用auto register 和 extern等修饰符,只能使用static修饰符;函数也不能用extern修饰符
类域中的静态成员变量和静态成员函数都具有外部的连接属性
类域小于文件域 大于函数域
类A的某个成员m在下列情况下具有类A的作用域
a m出现在a的某个成员函数中,且成员函数未定义同名标识符
b a.m 表达式
c pa->m 表达式 pa为指向a对象的指针
d A::m
5.8局部类和嵌套类
函数体内定义的类称为局部类 局部类中只能使用它的外围作用域中的对象和函数进行联系,
局部类中不能说明静态成员函数,所有成员函数必须定义在类体内。 不常用
int a;
void fun() //函数
{
static int s;
class A //类
{
public:
void init(int i){s=i;}
};
A m;
m.init(10); //A类的对象m与函数fun联系
}
嵌套类
一个类中定义的类称为嵌套类 定义嵌套类的类称为外围类。
主要目的是为了隐藏类名,减少全局的标识符。提高类的抽象能力,建立类间的主从关系
class A //外围类
{
public:
class B //嵌套类
{
public:
void bf(){...}; //成员函数
private:
};
void f();
private:
int a;
}
说明:
a B被隐藏在A中 B只能在A中使用,或者使用作用域运算符 A::B
b B的访问权限与f()函数是相同的都是public
c bf()函数只能在B中定义 不能在B之外定义
d B类中的成员不是A类的成员,反之一样;B的成员函数对A的成员变量没有访问权,反之一样。
综上:嵌套类可以看作普通的非嵌套类来处理。 嵌套类仅仅是语法上的嵌入 不影响功能 可以如下写法:
class A
{
...
}
class B
{
...
}
5.9对象的生存期
不同的存储对象生存期不同
对象从被创建开始到被释放为止的存在时间
a 局部对象:对象被构造函数初始化创建时开始 到函数或者函数体结束 调用析构函数 释放该对象 函数或者程序块内
b 静态对象:程序第一次执行所定义的静态对象时,到程序结束,对象被释放 文件中
c 全局对象:程序开始时,创建该对象 程序结束时释放对象 定义在文件中,包含在该文件的整个程序
案例
#include <iostream>
#include <string.h>
using namespace std;
class A //类声明 实现
{
public:
A(char * st);
~A();
private:
char string[50];
};
A::A(char * st)
{
strcpy(string,st);
cout<<"constructor called for"<<string<<endl;
}
A::~A()
{
cout<<"Destructor called for"<<string<<endl;
}
void fun()
{
A FunObject("FunObject"); //局部对象
static A staticObject("staticobject"); //静态对象
cout<<"in fun()."<<endl;
}
A globalobject("globalobject"); //全局对象
int main()
{
A mainobject("mainobject"); //局部对象
cout<<"in main(),before called fun\n";
fun(); //函数调用
cout<<"in main(),after called fun\n";
return 0;
}
练习:
一:
1.抽象类 模拟
2.class A
{
public:
private:
protected:
}
3.4.
5. :: 作用域运算符 标明变量的归属
选择:
1.a 2.c 3.d 4.d 5.d 6.a 7.c 8.a 9.d 10.
二
claas 成员缺省访问权限为私有
:: 运算符用来限定成员函数所属的类
析构函数可以不为空 但是只能有一个
构造函数可以重载 析构不可重载
说明和定义对象时,前面加上类名即可,不用加上class关键字
三程序输出
1.default constructor called
constructor called
a=0 b=0
a=4,b=8
2.
a=7,b=9
#include <iostream>
using namespace std;
class B
{
public:
B();
B(int i,int j);
void printb();
private:
int a,b;
};
class A
{
public:
A();
A(int i,int j);
void printa();
private:
B c;
};
A::A(int i,int j):c(i,j){};//¶ÔÏóAµÄ¹¹Ô캯Êý
void A::printa()
{
c.printb();
}
B::B(int i,int j)
{
a=i;
b=j;
}
void B::printb()
{
cout<<"a="<<a<<";b="<<b<<endl;
}
int main()
{
A m(7,9);//设置断点 单步进入可以查看程序执行语句的顺序 此句仅调用A的构造函数 设置B对象c的a b值
m.printa(); //此句输出a=7, b=9 调用B的printb来输出
return 0;
}
三 所有对象共享此静态变量 类中的静态成员函数可以直接以类名来调用 静态成员变量直接用静态成员函数来调用
104
四
1035 789.504
五
//5.6 编程要求
#include <iostream>
#include <math.h>
using namespace std;
class sum
{
friend void youyuan(sum &a);
public:
sum(); //default
sum(int _x,int _y);
sum(int _x, int _y,int _z);
void print(int i=1);
static void jingtai();
private:
int x;
int y;
int z;
static int m;
};
int sum::m=1;
sum::sum()
{
x=y=0;
cout<<"default constructor!"<<"\n";
cout<<"x:"<<x<<";y:"<<y<<"\n";
}
sum::sum(int _x,int _y)
{
x=_x;y=_y;
cout<<"constructor #1!"<<"\n";
cout<<"x:"<<x<<";y:"<<y<<"\n";
}
sum::sum(int _x,int _y,int _z)
{
x=_x;y=_y;z=_z;
cout<<"constructor #2!"<<"\n";
cout<<"x="<<x<<";y="<<y<<";z="<<z<<"\n";
}
void sum::print(int i)
{
if(i==1) cout<<"默认参数:"<<i<<"\n";
else cout<<"非默认参数:"<<i<<"\n";
}
void youyuan(sum &a)
{
cout<<"member is"<<a.x<<":"<<a.y<<"\n";
}
void sum::jingtai()
{
cout<<"访问静态成员变量:"<<m<<"::fuck"<<"\n";
}
int main()
{
sum A; //构造重载
sum B(3,5);
sum C(3,5,8);
B.print(5);//成员函数非默认
A.print();//默认构造函数
C.jingtai();
youyuan(A);
youyuan(B);
return 0;
}