C++类的定义
静态数据成员
- 用关键字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 类名&对象名); // 复制构造函数 //... }; 类名::类(const 类名&对象名) // 复制构造函数的实现 { 函数体 }
#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& 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! */