传统C++回顾

传统C++回顾

主要参考菜鸟教程,用于回忆当时没有好好学习的C++98

Vector的学习

#include <iostream>
#include <vector>
using namespace std;

int main() {
  vector<int> myVector;

  myVector.push_back(3);
  myVector.push_back(7);
  myVector.push_back(11);
  myVector.push_back(5);

  cout << "Elements in the vector: ";
  for (int element : myVector) {
    cout << element << " ";
  }
  cout << endl;

  cout << "First Element: " << myVector[0] << endl;
  cout << "Second Element: " << myVector.at(1) << endl;
  cout << "Size of vector: " << myVector.size() << endl;

  myVector.erase(myVector.begin() + 2);

  cout << "Elements in the vector after erasing: ";
  for (int element : myVector) {
    cout << element << " ";
  }
  cout << endl;

  myVector.clear();
  cout << "Size of the vector after clearing: " << myVector.size() << endl;
  return 0;
}

C++面向对象

C++ 类与对象

  1. 类访问修饰符
  • public: 可以被外部访问
  • private: 只能在类内部访问
  • protected: 可以被子类访问

  1. 类构造函数 & 析构函数
  • 构造函数,作用是初始化对象
    • 默认构造函数 Line()
    • 带参数的构造函数 Line(double len)
    • 初始化列表 Line::Line( double len): length(len)
  • 析构函数,作用是释放对象
    • Line::~Line()

  1. 拷贝构造函数
  • 它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象
    • 通过使用另一个同类型的对象来初始化新创建的对象
    • 复制对象把它作为参数传递给函数
    • 复制对象,并从函数返回这个对象
      如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。

拷贝构造函数确保了在创建对象副本时正确地复制了动态分配的内存,并保持了原始对象和新对象之间的独立性。但这会带来性能上的损失,尤其是在处理大型对象或包含复杂资源(如动态分配内存、文件句柄、网络连接等)的对象时。每次拷贝构造函数被调用时,都会执行一次对象的复制操作,这可能会涉及昂贵的资源分配和复制过程。

为了减少性能损失,可以采取以下措施:

  • 使用引用传递:在函数参数中使用引用或常量引用来避免不必要的拷贝。
  • 移动语义:C++11引入了右值引用和移动构造函数,可以更高效地处理临时对象的拷贝,通过“偷走”资源而不是复制它们。
  • 优化拷贝构造函数:如果可能,优化拷贝构造函数的实现,避免不必要的复制操作。
  • 使用智能指针:例如,使用std::unique_ptr或std::shared_ptr来自动管理资源,这样可以减少手动内存管理的需要,并且可以更高效地处理对象的复制。

  1. C++友元函数

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

#include <iostream>
using namespace std;

class Box {
  double width;

public:
  friend void printWidth(Box box);
  void setWidth(double wid);
};

void Box::setWidth(double wid) { width = wid; }

void printWidth(Box box) { cout << "Width of box: " << box.width << endl; }

int main() {
  Box box;

  box.setWidth(10.0);
  printWidth(box);
  return 0;
}

  1. C++内联函数

inline 关键字是用于建议编译器对函数进行内联展开的。内联函数更适合于小函数,大函数的内联会导致代码体积显著增加。


  1. C++ this指针

在 C++ 中,this 指针指向当前对象的实例,每一个对象都能通过 this 指针来访问自己的地址,可以在类的成员函数中使用,它可以用来指向调用对象。

当一个对象的成员函数被调用时,编译器会隐式地传递该对象的地址作为 this 指针;友元函数没有 this 指针,因为友元不是类的成员,只有成员函数才有 this 指针

this指针必须显示调用的实例:

  • 当成员变量与函数参数重名时
class Box {
  double width;

public:
  void setWidth(double width) {
    this->width = width; // 使用this来区分成员变量和参数
  }
};
  • 返回当前对象的引用
#include <iostream>
using namespace std;

class Box {
  double width;
  double height;

public:
  Box& setWidth(double width) {
    this->width = width;
    return *this;
  }

  Box& setHeight(double height) {
    this->height = height;
    return *this;
  }
};

int main() {
  Box box;
  box.setHeight(10.0).setWidth(20.0); // 链式调用
  return 0;
}
  • 在函数内部将当前对象作为参数传递
#include <iostream>
using namespace std;

class Box {
  double width;

public:
  void print() { cout << "Box(" << width << ")" << endl; }

  void doSomething() {
    print();
    this->print();
  }

  void setWidth(double wid) { width = wid; }
};

int main() {
  Box box;

  box.setWidth(10.0);
  box.doSomething();

  return 0;
}

  1. C++ 指向类的指针
  • 声明和初始化指向类的指针
  • 动态分配内存
  • 指向类的指针作为函数参数
#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()
{
   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;
}
  1. C++ 类的静态成员
    我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本

静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符::来重新声明静态变量从而对它进行初始化。


  1. 静态成员函数
    如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符::就可以访问。

C++继承

// 基类
class Animal {
    // eat() 函数
    // sleep() 函数
};


//派生类
class Dog : public Animal {
    // bark() 函数
};
  1. 访问控制和继承
访问 public protected private
同一个类 yes yes yes
派生类 yes yse no
外部类 yes no no

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

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

  1. 继承类型

当一个类派生自基类,该基类可以被继承为 public、protectedprivate 几种类型。

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

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

  1. 多继承

多继承即一个子类可以有多个父类,它继承了多个父类的特性。

#include <iostream>
using namespace std;

class Shape {
public:
  void setWidth(int w) { width = w; }
  void setHeight(int h) { height = h; }

protected:
  int width;
  int height;
};

class PaintCost {
public:
  int getCost(int area) { return area * 70; }
};

class Rectangle : public Shape, public PaintCost {
public:
  int getArea() { return width * height; }
};

int main() {
  Rectangle Rect;
  int area;

  Rect.setWidth(5);
  Rect.setHeight(7);

  area = Rect.getArea();

  // 输出对象的面积
  cout << "Total area: " << Rect.getArea() << endl;

  // 输出总花费
  cout << "Total paint cost: $" << Rect.getCost(area) << endl;

  return 0;
}

  1. 重载运算符和重载函数

C++ 允许在同一作用域中的某个函数运算符指定多个定义,分别称为函数重载运算符重载

重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。

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

Box operator+(const Box&);

声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:

Box operator+(const Box&, const Box&);
#include <iostream>
using namespace std;

class Box {
public:
  double getVolume() { return length * breadth * height; }
  void setLength(double len) { length = len; }
  void setBreadth(double bre) { breadth = bre; }
  void setHeight(double hei) { height = hei; }

  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;
}

::tips::

  • . 运算符用于访问对象的成员(数据成员或成员函数)当你有一个对象实例的引用时,使用 . 来访问其成员。
  • -> 运算符主要用于通过指针访问对象的成员,当你有一个指向对象的指针时,使用 -> 来访问其成员。
  • :: 运算符称为作用域解析运算符,用于访问类的静态成员、枚举的成员或者全局作用域中的变量或函数。当你需要明确指出是全局作用域或者静态成员时使用 ::

C++多态

多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。

静态多态:重载函数和运算符重载
动态多态:虚函数

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: " << width * height << 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: " << (width * height / 2) << endl;
    return (width * height / 2);
  }
};

int main() {
  Shape *shape;
  Rectangle rec(10, 7);
  Triangle tri(10, 5);

  shape = &rec;
  shape->area();

  shape = &tri;
  shape->area();

  return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Parent class area :
Parent class area :

导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。

但现在,让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 virtual,如下所示:

class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      virtual int area()
      {
         cout << "Parent class area :" <<endl;
         return 0;
      }
};
Rectangle class area: 70
Triangle class area: 25

此时,编译器看的是指针的内容,而不是它的类型。因此,由于 tri 和 rec 类的对象的地址存储在 *shape 中,所以会调用各自的 area() 函数。

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

虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定

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

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 告诉编译器,函数没有主体,上面的虚函数是纯虚函数

C++数据抽象与封装

#include <iostream>
using namespace std;

class Adder {
public:
  Adder(int i = 0) { total = i; }
  void addNum(int number) { total += number; }
  int getTotal() { return total; }

private:
  int total;
};

int main() {
  Adder a;

  a.addNum(10);
  a.addNum(20);

  cout << "total:" << a.getTotal() << endl;
  return 0;
}

C++接口(抽象类)

#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() {
  Rectangle Rect;
  Triangle Tri;

  Rect.setWidth(5);
  Rect.setHeight(7);

  cout << "Total Rectangle area: " << Rect.getArea() << endl;

  Tri.setWidth(5);
  Tri.setHeight(7);

  return 0;
}

C++模板编程

#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;
}
posted @   真真夜夜  阅读(3)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示