文章目录
静态数据成员
- 用关键字static声明
- 为该类的所有对象共享,静态数据成员具有静态生存期
- 必须在类外定义和初始化,用(::)来指明所属的类
class 类名称
{
// 在关键字public后面声明的是共有类型成员,类与外部的接口,任何外部函数都可以访问共有类型数据和函数。类(class)中只有声明,定义在外部定义
public:
共有成员(外部接口)
//在关键字private后面声明的是私有类型成员,只允许本类中的函数访问,而类外部的任何函数都不能访问。
//如果紧跟在类名称的后面(在前面的class后面的类名称后,即大括号里面声明的第一个)声明私有成员,则关键字private可以省略。
private:
私有成员
//与private相似,其差别表现在继承与派生时对派生类的影响不同。
protected:
保护型成员
};
类的成员函数
- 在类中声明函数原型;
- 可以在类外给出函数体实现,并在函数名前使用类名加以限定;
- 也可以直接在类中给出函数体,形成内联成员函数;
- 允许声明重载函数和带默认参数值的函数。
内联成员函数
- 为了提高运行时的效率,对于简单的函数可以声明为内联形式。
- 内联函数体中不要有复杂结构(如循环语句和switch语句)。
- 在类中声明内联函数的方式
-1.将函数体放在类的声明中
-2.使用inline关键字
#include <iostream>
using namespace std;
class Clock {
public:
void setTime(int newH = 0, int newM = 0, int newS = 0);
void showTime();
private:
int hour, minute, second;
};
void Clock::setTime(int newH, int newM, int newS) {
hour = newH;
minute = newM;
second = newS;
}
void Clock::showTime() {
cout << hour << minute << second;
}
int main()
{
Clock myClock;
myClock.setTime(8, 30, 30);
myClock.showTime();
return 0;
}
// 两种方法,两种定义方式。
class Point
{
public:
void Init(int initX, int initY)
{
X = initX;
Y = initY;
}
int getX() { return X; }
int GetY() { return Y; }
private:
int X, Y;
};
class Point
{
public:
void Init(int initX, int initY);
int GetX();
int GetY();
private:
int X, Y;
};
inline void Point::Init(int initX, int initY)
{
X = initX;
Y = initY;
}
inline int Point::GetX()
{
return X;
}
inline int Point::GetY()
{
return Y;
}
构造函数
在对象被创建时使用特定的值构造对象,或者将对象初始化为一个特定的初始状态
- 函数名与类名相同
- 不能定义返回值类型,也不能有return语句,但也不能用void
- 不能人为调用,只能系统调用
- 可以有形式参数,也可以没有形式参数
- 可以是内联函数
- 可以重载
- 可以带默认参数值
默认构造函数
调用时可以不需要实参的构造函数
-
参数表为空的构造函数
-
全部参数都有默认值的构造函数
-
构造函数调用顺序:先调用内嵌对象的构造函数(按内嵌时的声明顺序,先声明者先构造)。然后调用本类的构造函数。(析构函数的调用顺序相反)
-
若调用默认构造函数(即无形参的),则内嵌对象的初始化也将调用相应的默认构造函数。
构造函数例子
#include <iostream>
using namespace std;
class Clock {
public:
Clock(int newH, int newM, int newS); // 构造函数(函数名和类名相同)。
Clock(); // 默认构造函数
void setTime(int newH = 0, int newM = 0, int newS = 0);
void showTime();
private:
int hour, minute, second;
};
// 构造函数的实现:(不能规定返回类型return)
Clock::Clock(int newH, int newM, int newS) : // 初始化列表
hour(newH), minute(newM), second(newS) {}
Clock::Clock() :hour(0), minute(0), second(0) {} // 默认构造函数
void Clock::setTime(int newH, int newM, int newS) {
hour = newH;
minute = newM;
second = newS;
}
void Clock::showTime() {
cout << hour << minute << second << endl;
}
int main()
{
Clock c(8, 10, 0); // 自动调用构造函数
Clock c2;
c.showTime();
c2.showTime();
return 0;
}
委托构造函数
委托构造函数使用类的其他构造函数执行初始化过程。
例如
Clock(int newH, int newM, int newS):
hour(newH), minute(newM), second(newS) {}
Clock::Clock() : hour(0), minute(0), second(0) {}
Clock(int newH, int newM, int newS):
hour(newH), minute(newM), second(newS) {}
Clock: Clock(0, 0, 0) {}
复制构造函数定义(或者是拷贝构造函数)
复制构造函数是一种特殊的构造函数,其形参为本类对象引用。作用是用一个已存在的对象去初始化同类型的新对象。
复制构造函数被调用的三种情况
- 当用类的一个对象去初始化该类的另一个对象时
int main()
{
Point a(1, 2);
Point b(a);
Point c = a; // 这两句表达的意思相同。
}
- 如果函数的形参是类的对象,调用函数时,实现形参和实参结合时。
void f(Point p)
{
cout << p.getX() << endl;
}
int main()
{
Point a(1, 2);
f(a); // 函数的形参是类的对象,调用函数时,调用!
return 0;
}
// 只有把对象用值传递时,才会调用复制构造函数,如果传递引用,则不会调用复制构造函数。传递较大的对象时,传递引用比传值的效率高很多。
- 如果函数的返回值是类的对象,函数执行完成返回调用者时。
Point g() {
Point a(1, 2);
return a; // 函数的返回值是类的对象,返回函数值时,调用复制构造函数。
}
int main()
{
Point b;
b = g();
return 0;
}
class 类名{
public:
类名(形参); // 构造函数
类名(const 类名&amp;对象名); // 复制构造函数
//...
};
类名::类(const 类名&amp;对象名) // 复制构造函数的实现
{ 函数体 }
#include <iostream>
using namespace std;
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&amp; p) {
x = p.x;
y = p.y;
cout << "Calling the copy constructor" << endl;
}
// 形参为Point类对象的函数
void fun1(Point p) {
cout << p.getX() << endl;
}
// 返回值为Point类对象的函数
Point fun2() {
Point a(1, 2);
return a;
}
int main() {
Point a; // 第一个对象A
Point b = a; // 情况一,用A初始化B。第一次调用拷贝构造函数
cout << b.getX() << endl;
fun1(b); // 情况二,对象B作为fun1的实参。第二次调用拷贝构造函数
b = fun2(); // 情况三,函数的返回值是类对象,函数返回时,调用拷贝构造函数
cout << b.getX() << endl;
return 0;
}
析构函数
- 析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,以区别于构造函数。
- 它不能带任何参数,也没有返回值(包括void类型)。
- 只能有一个析构函数,不能重载。
- 如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。
#include <iostream>
using namespace std;
class Point {
public:
Point(int xx, int yy);
~Point();
// 其他函数...
private:
int x, y;
};
Point::Point(int xx, int yy)
{
x = xx;
y = yy;
}
Point::~Point() {
}
// 其他略...
类的组合
- 类中的成员是另一个类的对象
- 可以在已有抽象的基础上实现更复杂的抽象
#include <iostream>
using namespace std;
class Piont
{
private:
float x, y;// 点的坐标
public:
Point(float h, float v); // 构造函数
float GetX(void);
float GetY(void);
void Draw(void); // 在x,y处画点
};
class Line
{
private:
Point p1, p2; // 线段的两个端点
public:
Line(Point a, Point b); // 构造函数
void Draw(void); // 画出线段
};
类组合的构造函数的设计
原则:不仅要负责对本类中的基本类型成员赋初值,也要对对象成员初始化。
声明形式:
类名::类名(对象成员所需的形参,本类成员形参)
:对象1(参数),对象2(参数),......
{ 本类初始化 }
#include <math.h>
#include <iostream>
using namespace std;
class Point { // 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;
cout << "Calling the copy constructor of Point" << endl;
}
// 类的组合
class Line { // Line的定义
public:
Line(Point xp1, Point xp2);
Line(Line& l);
double getLen() { return len; }
private:
Point p1, p2;
double len;
};
Line::Line(Point xp1, Point xp2) :p1(xp1), p2(xp2) { // 相当于p1 = xp1。
cout << "Calling constructor of Line" << endl;
// 把后面的强制转化成double类型(静态转换)
double x = static_cast <double> (p1.getX() - p2.getX());
double y = static_cast <double> (p1.getY() - p2.getY());
len = sqrt(x * x + y * y);
}
Line::Line(Line& l) :p1(l.p1), p2(l.p2) {
cout << "Calling the copy constructor of Line" << endl;
len = l.len;
}
int main()
{
Point myp1(1, 1), myp2(4, 5);
Line line(myp1, myp2); // 1,myp1给xp1,然后到了Point;2,从右到左,先myp2
Line line2(line);
cout << "The length of the line is :";
cout << line.getLen() << endl;
cout << "The length of the line2 is :";
cout << line2.getLen() << endl;
return 0;
}
构造组合类对象时的初始化次序
- 首先对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员)进行初始化,初始化次序是成员在类体中定义的次序
成员对象构造函数调用顺序:按对象成员的定义顺序,先声明者先构造。
初始化成员列表中未出现的成员对象,调用默认构造函数(即无形参的)初始化
- 处理完初始化列表后,在执行构造函数的函数体。
前向引用声明
- 类要先声明,后使用
- 如果需要在某个类的声明之前,引用该类,则应进行前向引用声明
- 前向引用声明只为程序引入一个标识符,但具体声明在其他地方
class B; // 前向引用声明
class A {
public:
void f(B, b);
};
class B {
public:
void g(A, a);
};
注意事项
- 在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象
- 当使用前向引用声明时,只能使用被声明的符号,而不能涉及类的任何细节。
前向引用声明并不是万能的。需要注意的是,尽管使用了前向引用声明,但是在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象。例如:
class Fred; // 前向引用声明
class Barney {
Fred x; // 错误:类Fred的声明尚不完善
};
class Fred {
Barney y;
}
class Fred;
class Barney{
Fred *x; // 正确。。。
};
class Fred{
Barney y;
};
class Fred; // 前向引用声明
class Barney {
public:
void method()
{
x << yabbaDabbaDo(); // 错误: Fred类的对象在定义之前被使用
}
private:
Fred* x; // 正确,经过前向引用声明,可以声明Fred类的对象指针
};
class Fred{
public:
void tabbaDabbaDo();
private:
Barney * y;
};
UML:
事情(Things)
关系(Relationships)
图(Diagrams)
枚举类定义
enum class 枚举类型名: 底层类型{枚举值列表};
枚举类的优势
- 强作用域,其作用域限制在枚举类中;
- 例:使用Type的枚举值General
Type::General
- 转换限制,枚举类对象不可以与整形隐式地相互转换;
- 可以指定底层类型
- 例:enum class Type: char{General, Light, Medium, Heavy};
这个就说char类型的,不写表示int
#include <iostream>
using namespace std;
enum class Side {Right, Left};
enum class Thing{Wrong, Right}; // 不冲突
int main()
{
Side s = Side::Right;
Thing w = Thing::Wrong;
cout << (s == w) << endl; // 编译错误,无法直接比较不同枚举类
return 0;
}
实例:
#include <iostream>
using namespace std;
enum CPU_Rank { P1 = 1, P2, P3, P4, P5, P6, P7 };
class CPU {
private:
CPU_Rank rank;
int frequency;
float voltage;
public:
CPU(CPU_Rank r, int f, float v)
{
rank = r;
frequency = f;
voltage = v;
cout << "构造函数CPU!" << endl;
}
CPU(CPU& c)
{
rank = c.rank;
frequency = c.frequency;
voltage = c.voltage;
cout << "拷贝了构造函数CPU!" << endl;
}
~CPU() { cout << "析构函数CPU!" << endl; }
CPU_Rank GetRank() const { return rank; }
int GetFrequency() const { return frequency; }
float GetVoltage() const { return voltage; }
void SetRank(CPU_Rank r) { rank = r; }
void SetFrequency(int f) { frequency = f; }
void SetVoltage(float v) { voltage = v; }
void Run() { cout << "CPU starts to run!" << endl; }
void Stop() { cout << "CPU is stoped!" << endl; }
};
enum RAM_Type { DDR2 = 2, DDR3, DDR4 };
class RAM {
private:
enum RAM_Type type;
unsigned int frequency; // MHz
unsigned int size; // GB
public:
RAM(RAM_Type t, unsigned int f, unsigned int s)
{
type = t;
frequency = f;
size = s;
cout << "构造函数RAM!" << endl;
}
RAM(RAM& r)
{
type = r.type;
frequency = r.frequency;
size = r.size;
cout << "拷贝了构造函数RAM!" << endl;
}
~RAM() { cout << "析构函数RAM!" << endl; }
RAM_Type GetType() const { return type; }
unsigned int GetFrequency() const { return frequency; }
unsigned int GetSize() const { return size; }
void SetType(RAM_Type t) { type = t; }
void SetFrequency(unsigned int f) { frequency = f; }
void SetSize(unsigned int s) { size = s; }
void Run() { cout << "RAM starts to run!" << endl; }
void Stop() { cout << "RAM is stoped!" << endl; }
};
enum CDROM_Interface { SATA, USB };
enum CDROM_Install_type { external, built_in };
class CD_ROM {
private:
enum CDROM_Interface interface_type;
unsigned int cache_size; // MB
CDROM_Install_type install_type;
public:
CD_ROM(CDROM_Interface i, unsigned int s, CDROM_Install_type it)
{
interface_type = i;
cache_size = s;
install_type = it;
cout << "构造函数CD_RAM!" << endl;
}
CD_ROM(CD_ROM& cd)
{
interface_type = cd.interface_type;
cache_size = cd.cache_size;
install_type = cd.install_type;
cout << "拷贝了构造函数CD_RAM!" << endl;
}
~CD_ROM() { cout << "析构函数CD_ROM!" << endl; }
CDROM_Interface GetInterfaceType() const { return interface_type; }
unsigned int GetSize() const { return cache_size; }
CDROM_Install_type GetStallType() const { return install_type; }
void SetType(CDROM_Interface i) { interface_type = i; }
void SetFrequency(unsigned int s) { cache_size = s; }
void SetStallType(CDROM_Install_type it) { install_type = it; }
void Run() { cout << "CDROM starts to run!" << endl; }
void Stop() { cout << "CDROM is stoped!" << endl; }
};
class COMPUTER {
private:
CPU my_cpu;
RAM my_ram;
CD_ROM my_cdrom;
unsigned int storage_size; // GB
unsigned int bandwidth; // MB
public:
COMPUTER(CPU c, RAM r, CD_ROM cd, unsigned int s, unsigned int b);
~COMPUTER() { cout << "析构COMPUTER!" << endl; }
void Run() {
my_cpu.Run();
my_ram.Run();
my_cdrom.Run();
cout << "COMPUTER开始运行!" << endl;
}
void Stop()
{
my_cpu.Stop();
my_ram.Stop();
my_cdrom.Stop();
cout << "COMPUTER开始关机!" << endl;
}
};
COMPUTER::COMPUTER(CPU c, RAM r, CD_ROM cd, unsigned int s, unsigned int b) :my_cpu(c), my_ram(r), my_cdrom(cd)
{
storage_size = s;
bandwidth = b;
cout << "构造了一个COMPUTER!" << endl;
}
int main()
{
CPU a(P4, 300, 2.4);
a.Run();
a.Stop();
cout << "*********************************\n";
RAM b(DDR3, 1600, 8);
b.Run();
b.Stop();
cout << "*********************************\n";
CD_ROM c(SATA, 2, built_in);
c.Run();
c.Stop();
cout << "*********************************\n";
COMPUTER my_computer(a, b, c, 128, 10);
cout << "*********************************\n";
my_computer.Run();
my_computer.Stop();
cout << "*********************************\n";
return 0;
}
/*
构造函数CPU!
CPU starts to run!
CPU is stoped!
*********************************
构造函数RAM!
RAM starts to run!
RAM is stoped!
*********************************
构造函数CD_RAM!
CDROM starts to run!
CDROM is stoped!
*********************************
拷贝了构造函数CD_RAM!
拷贝了构造函数RAM!
拷贝了构造函数CPU!
拷贝了构造函数CPU!
拷贝了构造函数RAM!
拷贝了构造函数CD_RAM!
构造了一个COMPUTER!
析构函数CPU!
析构函数RAM!
析构函数CD_ROM!
*********************************
CPU starts to run!
RAM starts to run!
CDROM starts to run!
COMPUTER开始运行!
CPU is stoped!
RAM is stoped!
CDROM is stoped!
COMPUTER开始关机!
*********************************
析构COMPUTER!
析构函数CD_ROM!
析构函数RAM!
析构函数CPU!
析构函数CD_ROM!
析构函数RAM!
析构函数CPU!
*/