C++面向对象必会的基础知识点

    科技2024-04-21  9

    C++必会的基础知识点

    类&对象

    类的成员函数

    类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。

    代码

    class Box { public: double length; double breadth; double height; void move(); double getVolume() { return length * breadth * height; } }; class SmallBox:Box { public: void setLength(double len); double getLength(); SmallBox(double len); //构造函数 SmallBox(const SmallBox &obj); //拷贝构造函数 ~SmallBox(); //析构函数 private: double length1; double *ptr; };

    构造函数

    当对象被创建时执行

    //构造函数 SmallBox::SmallBox(double len): length1(len) //初始化列表 { //length1 = len; //初始化列表写法等价于这个 cout<<"SmallBox对象被创建了"<<endl; cout<<"初始化的len:"<<len<<endl; ptr = new double; *ptr = len; }

    析构函数

    析构函数会在每次删除所创建的对象时执行

    //析构函数会在每次删除所创建的对象时执行 SmallBox::~SmallBox() { cout<<"析构函数执行了,length:"<<*ptr<<endl; cout<<"释放内存"<<endl; delete ptr; }

    拷贝函数

    通俗的理解,就是把一个类赋值给另一个类

    //如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数 SmallBox::SmallBox(const SmallBox &obj) { cout<<"调用拷贝函数并且为指针ptr分配内存"<<endl; //为指针分配内存 ptr = new double; *ptr = *obj.ptr; }

    使用:

    SmallBox sbox1(7.7); //此时会调用拷贝函数 SmallBox box2 = sbox1; SmallBox box3 = sbox1;

    友元函数

    友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,用friend关键字修饰

    声明友元函数:

    class Box { double width; public: double length; friend void printWidth( Box box ); void setWidth( double wid ); };

    定义友元函数:

    // 请注意:printWidth() 不是任何类的成员函数 void printWidth( Box box ) { /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */ cout << "Width of box : " << box.width <<endl; }

    声明友元类:

    friend class ClassTwo;

    内联函数

    若一个函数是内联函数,编译器则会把该函数的副本放置在每个调用此函数的地方,当次函数进行任何修改,必须要重新编译,否则将使用旧的代码,定义内联函数,需要在函数名前放置关键字inline,在调用函数之前需要对函数进行定义。如果已定义的函数多于一行,编译器会忽略 inline 限定符

    在类定义中的定义的函数都是内联函数,即使没有使用 inline 说明符。

    this指针

    每个对象都可以用this指针来指向自己,指针是所有成员函数的隐含参数,在成员函数内部,它可以用来指向调用对象

    友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。

    示例代码:

    #include <iostream> using namespace std; class Box { public: // 构造函数定义 Box(double l=2.0, double b=2.0, double h=2.0) { cout <<"Constructor called." << endl; length = l; breadth = b; height = h; } double Volume() { return length * breadth * height; } int compare(Box box) { return this->Volume() > box.Volume(); } private: double length; // Length of a box double breadth; // Breadth of a box double height; // Height of a box }; int main(void) { Box Box1(3.3, 1.2, 1.5); // Declare box1 Box Box2(8.5, 6.0, 2.0); // Declare box2 if(Box1.compare(Box2)) { cout << "Box2 is smaller than Box1" <<endl; } else { cout << "Box2 is equal to or larger than Box1" <<endl; } return 0; }

    运行结果:

    Constructor called. Constructor called. Box2 is equal to or larger than Box1

    指向类的指针

    示例代码:

    #include <iostream> using namespace std; class Box { public: // 构造函数定义 Box(double l=2.0, double b=2.0, double h=2.0) { cout <<"Constructor called." << endl; length = l; breadth = b; height = h; } double Volume() { return length * breadth * height; } private: double length; // Length of a box double breadth; // Breadth of a box double height; // Height of a box }; int main(void) { Box Box1(3.3, 1.2, 1.5); // Declare box1 Box Box2(8.5, 6.0, 2.0); // Declare box2 Box *ptrBox; // Declare pointer to a class. // 保存第一个对象的地址 ptrBox = &Box1; // 现在尝试使用成员访问运算符来访问成员 cout << "Volume of Box1: " << ptrBox->Volume() << endl; // 保存第二个对象的地址 ptrBox = &Box2; // 现在尝试使用成员访问运算符来访问成员 cout << "Volume of Box2: " << ptrBox->Volume() << endl; return 0; }

    执行结果

    Constructor called. Constructor called. Volume of Box1: 5.94 Volume of Box2: 102

    类的静态成员

    我们可以用static关键字来把类成员定义成静态的,当我们这样做之后,意味着无论我们常见多少对象,静态成员在内存中始终只有一个,或者说,所有的对象公用同一个静态成员。

    我们不能把静态成员的初始化放置在类的定义中,可以在类的外部通过 :: 来重新申明静态变量从而对它进行初始化

    静态成员变量:

    示例代码:

    #include <iostream> using namespace std; class Box { public: static int objectCount; Box() { // 每次创建对象时增加 1 objectCount++; } }; // 初始化类 Box 的静态成员 int Box::objectCount = 0; int main(void) { Box Box1(); // 声明 box1 Box Box2(); // 声明 box2 // 输出对象的总数 cout << "Total objects: " << Box::objectCount << endl; return 0; }

    执行结果:

    Total objects: 2

    静态成员函数:

    +静态函数只要使用类名加范围解析运算符 :: 就可以访问静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。

    静态成员函数与普通成员函数的区别:

    静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。

    示例代码:

    #include <iostream> using namespace std; class Box { public: static int objectCount; // 构造函数定义 Box(double l=2.0, double b=2.0, double h=2.0) { cout <<"Constructor called." << endl; length = l; breadth = b; height = h; // 每次创建对象时增加 1 objectCount++; } double Volume() { return length * breadth * height; } static int getCount() { return objectCount; } private: double length; // 长度 double breadth; // 宽度 double height; // 高度 }; // 初始化类 Box 的静态成员 int Box::objectCount = 0; int main(void) { // 在创建对象之前输出对象的总数 cout << "Inital Stage Count: " << Box::getCount() << endl; Box Box1(3.3, 1.2, 1.5); // 声明 box1 Box Box2(8.5, 6.0, 2.0); // 声明 box2 // 在创建对象之后输出对象的总数 cout << "Final Stage Count: " << Box::getCount() << endl; return 0; }

    执行结果:

    Inital Stage Count: 0 Constructor called. Constructor called. Final Stage Count: 2

    继承

    概念

    当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。

    假设有一个基类 Shape,Rectangle 是它的派生类

    // 基类 class Shape {...} // 派生类 class Rectangle: public Shape {...}

    访问控制和继承

    派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。

    一个派生类继承了所有的基类方法,但下列情况除外:

    基类的构造函数、析构函数和拷贝构造函数。基类的重载运算符。基类的友元函数。

    继承类型

    我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

    公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

    多继承

    通俗的说,即一个子类有多个父类,有多个父类的特性

    语法格式:

    父类之间用逗号隔开

    class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,… { <派生类类体> };

    示例:

    // 基类 Shape class Shape {...} // 基类 PaintCost class PaintCost {...} // 派生类 class Rectangle: public Shape, public PaintCost{...}

    重载运算符和重载函数

    函数重载的基本概念

    同名,但是形参不同(参数个数、类型或顺序不同)的函数构成重载

    示例:

    #include <iostream> using namespace std; class printData { public: void print(int i) { cout << "整数为: " << i << endl; } void print(double f) { cout << "浮点数为: " << f << endl; } void print(char c[]) { cout << "字符串为: " << c << endl; } }; int main(void) { printData pd; // 输出整数 pd.print(5); // 输出浮点数 pd.print(500.263); // 输出字符串 char c[] = "Hello C++"; pd.print(c); return 0; }

    执行结果:

    整数为: 5 浮点数为: 500.263 字符串为: Hello C++

    运算符重载的概念

    重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。通俗来说,即重新定义运算符的功能

    Box operator+(const Box&);

    声明加法运算符用于把两个Box对象相加,因此,在使用的时候,需要传入两个Box对象

    Box operator+(const Box&, const Box&);

    示例:

    #include <iostream> using namespace std; class Box { public: double getVolume(void) { return length * breadth * height; } void setLength( double len ) { length = len; } void setBreadth( double bre ) { breadth = bre; } void setHeight( double hei ) { height = hei; } // 重载 + 运算符,用于把两个 Box 对象相加 Box operator+(const Box& b) { Box box; box.length = this->length + b.length; box.breadth = this->breadth + b.breadth; box.height = this->height + b.height; return box; } private: double length; // 长度 double breadth; // 宽度 double height; // 高度 }; // 程序的主函数 int main( ) { Box Box1; // 声明 Box1,类型为 Box Box Box2; // 声明 Box2,类型为 Box Box Box3; // 声明 Box3,类型为 Box double volume = 0.0; // 把体积存储在该变量中 // Box1 详述 Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // Box2 详述 Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0); // Box1 的体积 volume = Box1.getVolume(); cout << "Volume of Box1 : " << volume <<endl; // Box2 的体积 volume = Box2.getVolume(); cout << "Volume of Box2 : " << volume <<endl; // 把两个对象相加,得到 Box3 Box3 = Box1 + Box2; // Box3 的体积 volume = Box3.getVolume(); cout << "Volume of Box3 : " << volume <<endl; return 0; }

    可重载运算符/不可重载运算符

    下面是可重载的运算符列表:

    双目算术运算符+ (加),-(减),*(乘),/(除),% (取模)关系运算符==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于)逻辑运算符||(逻辑或),&&(逻辑与),!(逻辑非)单目运算符+ (正),-(负),*(指针),&(取地址)自增自减运算符++(自增),–(自减)位运算符| (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)赋值运算符=, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=空间申请与释放new, delete, new[ ] , delete[]其他运算符()(函数调用),->(成员访问),,(逗号),

    下面是不可重载的运算符列表:

    .:成员访问运算符., ->:成员指针访问运算符:::域运算符sizeof:长度运算符?::条件运算符#: 预处理符号

    多态

    顾名思义,即多种形态,例如:动物类,下面的子类有猫、狗和羊等到…,猫狗羊就是动物类的多种形态

    C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

    示例代码:

    #include <iostream> using namespace std; class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } int area() { cout << "Parent class area :" <<endl; return 0; } }; class Rectangle: public Shape{ public: Rectangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Rectangle class area :" <<endl; return (width * height); } }; class Triangle: public Shape{ public: Triangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Triangle class area :" <<endl; return (width * height / 2); } }; // 程序的主函数 int main( ) { Shape *shape; Rectangle rec(10,7); Triangle tri(10,5); // 存储矩形的地址 shape = &rec; // 调用矩形的求面积函数 area shape->area(); // 存储三角形的地址 shape = &tri; // 调用三角形的求面积函数 area shape->area(); return 0; }

    它会产生下列结果:

    Parent class area Parent class area

    明细,上面输出的结果有误,导致错误的原因是调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。也被称为早绑定,我们只需要在Shape类中,area()方法前添加virtual关键字即可;

    修改后的输出结果:

    Rectangle class area Triangle class area

    此时,编译器看的是指针的内容,而不是它的类型

    每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。有了多态,您可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。

    虚函数

    虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

    纯虚函数

    您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。

    虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。包含纯虚函数的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引用。

    我们可以把基类中的虚函数 area() 改写如下:

    class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } // pure virtual function virtual int area() = 0; };

    = 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数。

    接口(抽象类)

    概念

    如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 “= 0” 来指定的,如下所示

    class Box { public: // 纯虚函数 virtual double getVolume() = 0; private: double length; // 长度 double breadth; // 宽度 double height; // 高度 };

    抽象类的子类必须重写所有的纯虚函数

    抽象类的示例:

    #include <iostream> using namespace std; // 基类 class Shape { public: // 提供接口框架的纯虚函数 virtual int getArea() = 0; void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } protected: int width; int height; }; // 派生类 class Rectangle: public Shape { public: int getArea() { return (width * height); } }; class Triangle: public Shape { public: int getArea() { return (width * height)/2; } }; int main(void) { Rectangle Rect; Triangle Tri; Rect.setWidth(5); Rect.setHeight(7); // 输出对象的面积 cout << "Total Rectangle area: " << Rect.getArea() << endl; Tri.setWidth(5); Tri.setHeight(7); // 输出对象的面积 cout << "Total Triangle area: " << Tri.getArea() << endl; return 0; }

    它会产生下面的结果:

    Total Rectangle area: 35 Total Triangle area: 17

    模板

    模板是创建泛型类或者函数的蓝图或公式

    函数模板

    语法形式:

    template <typename type> ret-type func-name(parameter list) { // 函数的主体 }

    type 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用

    示例:

    #include <iostream> #include <string> using namespace std; template <typename T> inline T const& Max (T const& a, T const& b) { return a < b ? b:a; } int main () { int i = 39; int j = 20; cout << "Max(i, j): " << Max(i, j) << endl; double f1 = 13.5; double f2 = 20.7; cout << "Max(f1, f2): " << Max(f1, f2) << endl; string s1 = "Hello"; string s2 = "World"; cout << "Max(s1, s2): " << Max(s1, s2) << endl; return 0; }

    类模板

    语法格式:

    template <class type> class class-name { . . . }

    type 是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。

    示例:

    #include <iostream> #include <vector> #include <cstdlib> #include <string> #include <stdexcept> using namespace std; template <class T> class Stack { private: vector<T> elems; // 元素 public: void push(T const&); // 入栈 void pop(); // 出栈 T top() const; // 返回栈顶元素 bool empty() const{ // 如果为空则返回真。 return elems.empty(); } }; template <class T> void Stack<T>::push (T const& elem) { // 追加传入元素的副本 elems.push_back(elem); } template <class T> void Stack<T>::pop () { if (elems.empty()) { throw out_of_range("Stack<>::pop(): empty stack"); } // 删除最后一个元素 elems.pop_back(); } template <class T> T Stack<T>::top () const { if (elems.empty()) { throw out_of_range("Stack<>::top(): empty stack"); } // 返回最后一个元素的副本 return elems.back(); } int main() { try { Stack<int> intStack; // int 类型的栈 Stack<string> stringStack; // string 类型的栈 // 操作 int 类型的栈 intStack.push(7); cout << intStack.top() <<endl; // 操作 string 类型的栈 stringStack.push("hello"); cout << stringStack.top() << std::endl; stringStack.pop(); stringStack.pop(); } catch (exception const& ex) { cerr << "Exception: " << ex.what() <<endl; return -1; } }

    执行结果:

    7 hello Exception: Stack<>::pop(): empty stack
    Processed: 0.029, SQL: 9