C++是一门面向对象的语言,本篇就C++面向对象中类与对象的特性做相关整理
类与对象
类的对象和概念
变量 + 函数 进行捆绑封装形成一个类
-
定义类
class Employee { private: char sname[20]; public: int salary; void setName(const char *name); void getName(char *name); void avrageSalary(Employee e1, Employee e2); }; // 必须要有分号 // 成员变量的函数可在类中进行实现,同样也可在外部实现 // 外部实现方式 类名 :: 方法 void Employee ::setName(const char * name) { strcpy(sname, name); } void Employee ::getName(char *name) { strcpy(name, sname); }
-
使用类
Employee emp; // 对象名.成员名 emp.setName("Rick"); emp.salary = 10000; // 指针->成员名 Employee *p = &emp; p->salary = 5000; // 引用名.成员名 CEmployee &r = emp; r.salary = 8000;
访问权限
private | 私有成员,只能在成员函数中访问 |
---|---|
public | 公有成员,可以在任何地方访问 |
protect | 保护成员,暂不介绍 |
默认函数
-
构造函数
class Line{ public: Line(); // 这是默认空构造函数 Line(double length); // 带参构造函数 private: double length; }; int main( ){ Line line1; Line line2(5.0) }
-
析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
#include <iostream> using namespace std; class Line { public: void setLength( double len ); double getLength( void ); Line(); // 这是构造函数声明 ~Line(); // 这是析构函数声明 private: double length; }; // 成员函数定义,包括构造函数 Line::Line(void) { cout << "Object is being created" << endl; } Line::~Line(void) { cout << "Object is being deleted" << endl; } void Line::setLength( double len ) { length = len; } double Line::getLength( void ) { return length; } // 程序的主函数 int main( ) { Line line; // 设置长度 line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl; return 0; }
Object is being created Length of line : 6 Object is being deleted
this指针
并非对象的成员,是常量指针
每个对象可以使用this指针访问自己的地址
非static成员函数调用时,this指针为隐式参数
用途:防止自赋值、返回以连续调用
class Rick
{
private:
int age;
public:
Rick();
Rick(int age);
Rick* ReturnAddress() {
return this;
}
};
Rick::Rick(int age)
{
this->age = age;
}
函数模板
例如实际问题中的需要:对于不同类型数据可以用的排序函数sort
模板中class
也可写成 typename
template <class T>
return-type sort(...T...)
template <class T1, class T2>
template <class T>
void print(const T array[], int size){
int i;
for (i = 0; i < size; i++) {
cout << array[i];
}
return;
}
#include <iostream>
using namespace std;
template <class T>
T Max(T a, T b) {
cout << "TemplateMax" << endl;
return 0;
}
template <class T1, class T2>
T1 Max(T1 a, T2 b) {
cout << "TemplateMax2" << endl;
return 0;
}
double Max(double a, double b) {
cout << "MyMax" << endl;
return 0;
}
int main() {
int i = 4, j = 5;
Max(1.2, 3.4); // MyMax
Max(i, j); // TemplateMax
Max(1.2, 3); // TemplateMax2
return 0;
}
在有多个函数和函数模板名字相同的情况下,编译器如下处理:
- 先找参数完全匹配的普通函数(非由模板实例化而得的函数)
- 再找参数完全匹配的模板函数
- 再找是参数经过自动类型转换后能够匹配的普通函数
- 上面的都找不到,则报错
类模板
为了多快好省地定义出一批相似的类,可以定义类模板,然后由类模板生成不同的类
类模板:在定义类的时候给它一个/多个参数,这个/些参数表示不同的数据类型。在调用类模板时,指定参数,由编译系统根据参数提供的数据类型自动产生相应的模板类
#include <iostream>
using namespace std;
template <class T> // 类模板首部,声明类模板参数
class Carray
{
T *ptrElement;
int size;
public:
Carray(int length);
~Carray();
int len();
void setElement(T arg, int index);
T getElement(int index);
};
int main(int argc, char const *argv[])
{
// 创建一个元素类型为int的Carray模板类,而声明该模板类的一个对象、以及一个指针
Carray <int> arrayInt(50), *ptrArrayInt;
return 0;
}
函数模板作为类模板成员
#include <iostream>
using namespace std;
template <class T>
class A {
public:
// 成员模板函数
template <class T2>
void Func(T2 t) {
cout << t;
}
};
int main() {
A<int> a;
a.Func('K'); // K
a.Func("hello"); // hello
return 0;
}
类模板的“<类型参数表>”中可以出现非类型参数
template <class T, int size>
class Array {
T array[size];
public:
void Print() {
for (int i = 0; i < size; i++) {
cout << array[i] << endl;
}
}
};
int main() {
Array<double, 40> a1;
Array<int, 50> a2;
return 0;
}
static
-
static修饰成员变量就是静态成员变量,静态成员变量一共就一份,为所有对象共享,静态成员不需要通过对象就能访问到
-
sizeof
运算符不会计算静态成员变量class MyClass { int n; static int n; }; sizeof(MyClass) = 4
-
static修饰成员函数就是静态成员函数,普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象
#include <iostream> using namespace std; class Rectangle { private: int w,h; static int nTotalArea; static int nTotalNumber; public: Rectangle(int w_, int h_); ~Rectangle(); static void PrintTotal(); }; // 必须在定义类的文件中对静态成员变量进行一次说明或初始化,否则链接不能通过 int Rectangle::nTotalNumber = 0; int Rectangle::nTotalArea = 0; Rectangle::Rectangle(int w_, int h_) { w = w_; h = h_; nTotalNumber ++; nTotalArea += w * h; } Rectangle::~Rectangle() { nTotalNumber--; nTotalArea -= w * h; } /** * 在静态成员函数中,不能访问费静态成员变量,也不能调用非静态成员函数 */ void Rectangle::PrintTotal() { cout << nTotalNumber << "," << nTotalArea << endl; } int main(void){ Rectangle r1(3,3), r2(2,2); Rectangle::PrintTotal(); r1.PrintTotal(); return 0; }
成员变量和封闭类
有成员对象的类叫 封闭类
#include <iostream>
using namespace std;
class CTyre {
private:
int radius;
int width;
public:
// 构造器初始化列表
CTyre(int r, int w) : radius(r), width(w) {}
};
class CEngine{
};
class CCar {
private:
int price;
CTyre tyre;
CEngine engine;
public:
CCar(int p, int tr, int tw);
};
CCar::CCar(int p, int tr, int tw) : price(p), tyre(tr, tw) {}
int main(void) {
CCar car(20000, 17, 225);
return 0;
}
这个例子中如果CCar
不定义构造函数, CCar car
会报错,因为编译器不明白 car.tyre
该如何初始化。car.engine
的初始化没问题,用默认构造函数即可。
任何生成封闭类对象的语句,都要让编译器明白,对象中的成员对象是如何初始化的。
具体做法就是:通过封闭类的构造函数的初始化列表
常量对象、常量成员函数
-
常量对象:如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加
const
关键字class Demo { private: int value; public: void setValue() {} }; const Demo Obj; // 常量对象
-
常量成员函数:在函数说明后面加
const
关键字。常量成员函数执行期间不应修改其所作用的对象。因此,在常量成员函数中不能修改成员变量的值(静态成员变量除外)class Sample { public: int value; void GetValue() const; void func(); Sample(); }; void Sample::GetValue() const { // value = 0 wrong! // func(); wrong! } int main(void) { const Sample s; s.value = 100; // Error 常量对象不可修改 s.func(); // Error 常量对象上面不能执行非常量成员函数 s.GetValue(); // OK 常量对象上可以执行常量成员函数 return 0; }
-
常引用:引用前面加const关键字,称为常引用。不能通过其常引用,修改其引用的变量。
class Sample { ... } // 当我们去传递引用参数的时候,不能避免o无意被修改,这时就需要常引用 void PrintObj(Sample &o) { ... } // 这样就能确保不会出现无意中更改o值得语句 void PrintObj(const Sample &o) { ... }
友元
友元分为友元函数和友元类两种
-
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
#include <iostream> using namespace std; class Car; //提前声明Car类 class Driver { public: void ModifyCar(Car* pCar); }; class Car { private: int price; friend int MostExpensiveCar(Car cars[], int total); // 友元声明 // 友元声明:可以将一个类的成员函数(包括构造、析构函数)说明为另一个类的友元 friend void Driver::ModifyCar(Car* pCar); }; void Driver::ModifyCar(Car* pCar) { pCar->price += 1000; } // 不是任何类的成员函数 int MostExpensiveCar (Car cars[], int total) { int tmpMax = -1; for (int i = 0; i < total; i++) { if (cars[i].price >tmpMax) { tmpMax = cars[i].price; } } return tmpMax; }
-
友元类:如果A是B的友元类,那么A的成员函数可以访问B的私有成员。(友元类之间的关系不能传递,不能继承)
#include <iostream> using namespace std; class Car { private: int price; friend class Driver; // 声明Driver为友元类 }; class Driver { public: Car myCar; void ModifyCar() { myCar.price += 1000; } };