C++知识点复习下
- 一、面向对象编程:深入理解类与对象
- 1.类的定义和访问
- 2.this指针
- 3.构造函数与析构函数
- 4.重载和拷贝构造函数
- 5.常成员和静态成员
- 6.友元
- 二、运算符重载
- 1. 规则
- 2. 成员或友元重载
- 2.1 例子:成员函数重载
- 2.2 例子:友元函数重载
- 3. 类类型转换
- 3.1 构造函数类类型转换
- 3.2 转换运算符类类型转换
- 三、继承
- 1. 类之间的访问控制
- 1.1 公有继承
- 1.2 私有继承
- 1.3 保护继承
- 2. 基类初始化
- 3. 多继承
- 4. 虚继承
- 四、虚函数与多态性
- 1. 引言
- 2. 虚函数的概念
- 3. 静态联编与动态联编
- 3.1 静态联编
- 3.2 动态联编
- 4. 类指针与多态性
- 5. 纯虚函数与抽象类
- 5.1 纯虚函数
- 5.2 抽象类
- 五、模板
- 1. 引言
- 2. 函数模板
- 3. 类模板
- 4. 模板重载
一、面向对象编程:深入理解类与对象
在面向对象编程(Object-Oriented Programming,简称OOP)中,类与对象是基本概念,对于理解和设计复杂的软件系统至关重要。包括类的定义和访问、this指针、构造函数、析构函数、重载和拷贝构造函数、常成员和静态成员以及友元。
1.类的定义和访问
在C++中,类是一种用户自定义的数据类型,用于封装数据和相关操作。以下是一个简单的类的定义和对象的访问的示例:
// 类的定义
class Person {
public:// 成员变量string name;int age;// 成员函数void displayInfo() {cout << "Name: " << name << ", Age: " << age << endl;}
};// 对象的访问
int main() {// 创建对象Person person1;// 访问对象的成员person1.name = "John";person1.age = 25;person1.displayInfo();return 0;
}
2.this指针
在类的成员函数中,this指针用于指向当前对象的地址,以便访问对象的成员。这在类的方法中经常用于区分成员变量和参数变量。
class Point {
private:int x, y;public:void setCoordinates(int x, int y) {// 使用this指针来区分成员变量和参数变量this->x = x;this->y = y;}
};
3.构造函数与析构函数
构造函数在对象创建时调用,用于初始化对象的成员变量。析构函数在对象销毁时调用,用于清理资源。以下是一个简单的例子:
class Car {
public:// 构造函数Car() {cout << "Car is created." << endl;}// 析构函数~Car() {cout << "Car is destroyed." << endl;}
};
4.重载和拷贝构造函数
重载允许在同一作用域中定义多个相同名称的函数,但参数列表不同。拷贝构造函数用于在对象创建时从另一个对象中拷贝值。
class Complex {
public:// 重载构造函数Complex(int real, int imag) {this->real = real;this->imag = imag;}// 拷贝构造函数Complex(const Complex &obj) {real = obj.real;imag = obj.imag;}private:int real, imag;
};
5.常成员和静态成员
常成员是指在成员函数中不能修改成员变量的成员,通过关键字const
实现。静态成员是类的所有对象共享的成员。
class Circle {
public:// 静态成员static int count;// 常成员函数void displayRadius() const {// 不可修改成员变量// radius = 5; // 错误cout << "Radius: " << radius << endl;}private:int radius;
};// 初始化静态成员
int Circle::count = 0;
6.友元
友元允许一个外部类或函数访问另一个类的私有成员。这提供了灵活性,但也要慎用,因为可能破坏封装性。
class Square;class Rectangle {
private:int length, width;public:Rectangle(int l, int w) : length(l), width(w) {}// 友元函数friend bool isSquare(Rectangle, Square);
};class Square {
private:int side;public:Square(int s) : side(s) {}// 友元函数的实现friend bool isSquare(Rectangle, Square);
};// 友元函数
bool isSquare(Rectangle r, Square s) {return (r.length == s.side) && (r.width == s.side);
}
二、运算符重载
1. 规则
在C++中,运算符重载必须遵循一些规则,以确保正确和一致的行为。
- 重载的运算符必须至少有一个操作数是用户定义的类型。
- 不能改变运算符的优先级或结合性。
- 不能创建新的运算符。
- 只能重载已存在的运算符。
- 不能改变运算符的含义。
2. 成员或友元重载
运算符重载可以通过成员函数或友元函数来实现。对于成员函数重载,通常将运算符重载作为类的成员函数,这样它至少有一个操作数是调用对象。对于友元函数,它可以访问类的私有成员,但不是类的成员。
2.1 例子:成员函数重载
class Complex {
public:Complex operator+(const Complex& other) const {Complex result;result.real = this->real + other.real;result.imaginary = this->imaginary + other.imaginary;return result;}private:double real;double imaginary;
}
2.2 例子:友元函数重载
class Complex {
public:friend Complex operator+(const Complex& c1, const Complex& c2) {Complex result;result.real = c1.real + c2.real;result.imaginary = c1.imaginary + c2.imaginary;return result;}private:double real;double imaginary;
}
3. 类类型转换
类类型转换是将一个类类型转换为另一个类类型的过程。在C++中,有两种类类型转换:构造函数和转换运算符。
3.1 构造函数类类型转换
class Distance {
public:Distance(int meters) : meters(meters) {}private:int meters;
};class Speed {
public:Speed(const Distance& distance) {// Conversion logic from distance to speed}private:// Other members
};
3.2 转换运算符类类型转换
class Rational {
public:operator double() const {return static_cast<double>(numerator) / denominator;}private:int numerator;int denominator;
}
三、继承
1. 类之间的访问控制
在C++中,继承提供了三种访问控制方式:公有继承、私有继承和保护继承。这些方式决定了派生类如何访问基类的成员。
1.1 公有继承
公有继承是最常见的一种方式,它允许派生类访问基类的公有成员,但不能访问基类的私有成员。
class Base {
public:int publicMember;
private:int privateMember;
};class Derived : public Base {// 可以访问publicMember,但不能访问privateMember
};
1.2 私有继承
私有继承使得基类的公有和保护成员在派生类中变为私有成员,因此不能被外部访问。
class Base {
public:int publicMember;
protected:int protectedMember;
};class Derived : private Base {// publicMember和protectedMember在Derived中都变成私有成员
};
1.3 保护继承
保护继承允许派生类访问基类的公有和保护成员,但它们在派生类中仍然是保护成员。
class Base {
public:int publicMember;
protected:int protectedMember;
};class Derived : protected Base {// 可以访问publicMember和protectedMember
};
2. 基类初始化
在派生类的构造函数中,需要正确初始化基类的部分。这可以通过使用初始化列表来完成。
class Base {
public:Base(int value) : data(value) {}
private:int data;
};class Derived : public Base {
public:Derived(int value) : Base(value), derivedData(value * 2) {}
private:int derivedData;
};
3. 多继承
多继承允许一个派生类同时继承多个基类。在多继承中,需要注意解决潜在的命名冲突问题。
class Base1 {
public:void display() { /* 实现 */ }
};class Base2 {
public:void display() { /* 实现 */ }
};class Derived : public Base1, public Base2 {
public:// 需要解决display()的命名冲突using Base1::display;
};
4. 虚继承
虚继承用于解决菱形继承问题,其中一个派生类通过两个不同的路径继承同一个基类。使用虚继承可以确保只有一份基类的实例。
class Base {
public:int data;
};class Derived1 : virtual public Base {// 实现
};class Derived2 : virtual public Base {// 实现
};class DiamondDerived : public Derived1, public Derived2 {// 只有一份Base的实例
};
四、虚函数与多态性
1. 引言
在面向对象的程序设计中,虚函数和多态性是C++中强大而灵活的特性。它们使得代码更加可维护、可扩展,并提高了代码的复用性。多态性的实现方式,包括静态联编、类指针、动态联编、纯虚函数和抽象类。
2. 虚函数的概念
在C++中,虚函数通过将关键字virtual
添加到基类函数声明中来实现。虚函数的存在使得在运行时能够根据对象的实际类型调用相应的函数。
class Shape {
public:virtual void draw() {// 基类中的虚函数// 具体实现由派生类提供}
};
3. 静态联编与动态联编
3.1 静态联编
静态联编是指在编译时确定调用哪个函数,这是C++中默认的行为。对于非虚函数,编译器在编译时就能确定要调用的函数。
class Base {
public:void print() {std::cout << "Base class" << std::endl;}
};class Derived : public Base {
public:void print() {std::cout << "Derived class" << std::endl;}
};int main() {Derived derivedObj;derivedObj.print(); // 编译时确定调用Derived::print()return 0;
}
3.2 动态联编
动态联编是通过使用虚函数来实现的,它允许在运行时确定要调用的函数。使用基类指针或引用调用虚函数会触发动态联编。
int main() {Base* basePtr = new Derived();basePtr->print(); // 运行时确定调用Derived::print()delete basePtr;return 0;
}
4. 类指针与多态性
通过使用基类指针或引用,可以实现对派生类对象的多态操作。
void drawShape(Shape* shape) {shape->draw(); // 调用派生类中相应的draw函数
}int main() {Circle circle;Square square;drawShape(&circle); // 多态调用Circle::draw()drawShape(&square); // 多态调用Square::draw()return 0;
}
5. 纯虚函数与抽象类
5.1 纯虚函数
纯虚函数是在基类中声明的虚函数,但没有提供具体实现。包含纯虚函数的类被称为抽象类。
class AbstractShape {
public:virtual void draw() = 0; // 纯虚函数
};class ConcreteShape : public AbstractShape {
public:void draw() override {// 具体实现}
};
5.2 抽象类
抽象类不能实例化,但可以作为接口类,定义派生类必须实现的接口。
void drawAllShapes(std::vector<AbstractShape*>& shapes) {for (auto shape : shapes) {shape->draw(); // 多态调用各个派生类的draw函数}
}int main() {Circle circle;Square square;ConcreteShape concreteShape;std::vector<AbstractShape*> shapes = {&circle, &square, &concreteShape};drawAllShapes(shapes);return 0;
}
五、模板
1. 引言
C++模板是一种强大的编程工具,它允许在编写代码时使用泛型类型。泛型编程使得代码更加灵活和可重用,因为它允许编写与特定数据类型无关的通用代码。在C++中,主要有两种类型的模板:函数模板和类模板。此外,C++还支持模板的重载,使得我们可以根据不同的需求定义多个模板版本。
2. 函数模板
函数模板是一种允许定义通用函数的机制,可以处理不同类型的参数。下面是一个简单的函数模板示例:
template <typename T>
T add(T a, T b) {return a + b;
}int main() {int result_int = add(5, 10);double result_double = add(3.5, 2.7);return 0;
}
在上面的例子中,add
函数是一个模板函数,可以处理不同类型的参数,包括整数和浮点数。
3. 类模板
类模板允许我们定义通用类,可以处理不同类型的数据。以下是一个简单的类模板示例:
template <typename T>
class Pair {
public:Pair(T first, T second) : first_(first), second_(second) {}T getFirst() const {return first_;}T getSecond() const {return second_;}private:T first_;T second_;
};int main() {Pair<int> intPair(1, 2);Pair<double> doublePair(3.5, 2.7);return 0;
}
在这个例子中,Pair
类模板可以处理不同类型的数据,例如 int
和 double
。
4. 模板重载
模板重载允许我们定义多个模板版本以处理不同类型或不同数量的参数。以下是一个模板重载的示例:
template <typename T>
T multiply(T a, T b) {return a * b;
}template <typename T, typename U>
U multiply(T a, U b) {return a * b;
}int main() {int result1 = multiply(5, 10);double result2 = multiply(3.5, 2.7);return 0;
}
在这个例子中,有两个版本的 multiply
函数,分别处理相同类型和不同类型的参数。