我经过前期项目的学习和锻炼,现在开始准备学习C++相关的内容,也是为了之后的面试准备内容,C++相较于C语言来说,其实增加的只是几个特性而已,封装、继承、多态;以及C++其实是面向对象的语言,在接触过C#之后对类、对象的概念有了初步的理解,但还不够深刻,希望通过C++的学习可以加深对面向对象编程的理解和使用。
面向对象编程三大核心概念
封装
封装就是将数据(属性)和操作数据的方法(代码)绑定在一起,并隐藏对象的内部实现细节,仅仅暴露必要的接口(方法或者叫函数)给外界使用,确保数据的安全性和可靠性。
封装主要是通过private、public、protected来实现,下面是一个Person类的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <iostream> #include <string>
class Person { private: std::string name; int age;
public: Person(const std::string& name, int age) : name(name), age(age) {}
std::string getName() const { return name; } void setName(const std::string& name) { this->name = name; }
int getAge() const { return age; } void setAge(int age) { if(age >= 0) { this->age = age; } }
void displayInfo() const { std::cout << "Name: " << name << ", Age: " << age << std::endl; } };
int main() { Person person("Alice", 30); person.displayInfo();
person.setAge(35); person.displayInfo();
return 0; }
|
继承
继承是面向对象的另一个重要概念,一个类可以继承另一个类的属性和方法,从而实现代码的复用和层次化分类,子类不仅拥有父类的属性和方法,还可以自己进行相应的扩展和重写相关的功能模块,用以实现更加具体的行为。下面是继承的实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include <iostream> #include <string>
class Animal { protected: std::string name;
public: Animal(const std::string& name) : name(name) {}
void sleep() const { std::cout << name << " is sleeping." << std::endl; }
virtual void makeSound() const { std::cout << "Animal makes a sound." << std::endl; } };
class Dog : public Animal { public: Dog(const std::string& name) : Animal(name) {}
void bark() const { std::cout << name << " is barking." << std::endl; }
void makeSound() const override { std::cout << name << " barks." << std::endl; } };
int main() { Dog dog("Buddy"); dog.sleep(); dog.bark(); dog.makeSound();
return 0; }
|
多态
多态是面向对象编程的最后一个特性,多态可以使我们以相同的方法处理不同类型的对象,只要他们继承或者实现了相同的类型,简单说,就是子类型的指针赋值给父类型的指针,多态性在C++中主要是依靠虚函数来实现的,虚函数就是在子类中允许重新定义的成员函数,下面是多态的实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include <iostream> #include <string> #include <vector>
class Shape { protected: std::string color;
public: Shape(const std::string& color) : color(color) {}
virtual void draw() const { std::cout << "Drawing a " << color << " shape." << std::endl; }
virtual ~Shape() = default; };
class Circle : public Shape { private: double radius;
public: Circle(const std::string& color, double radius) : Shape(color), radius(radius) {}
void draw() const override { std::cout << "Drawing a " << color << " circle with radius " << radius << "." << std::endl; } };
class Rectangle : public Shape { private: double width, height;
public: Rectangle(const std::string& color, double width, double height) : Shape(color), width(width), height(height) {}
void draw() const override { std::cout << "Drawing a " << color << " rectangle of width " << width << " and height " << height << "." << std::endl; } };
int main() { std::vector<Shape*> shapes; shapes.push_back(new Circle("red", 5.0)); shapes.push_back(new Rectangle("blue", 3.0, 4.0));
for(const Shape* shape : shapes) { shape->draw(); }
for(Shape* shape : shapes) { delete shape; }
return 0; }
|
区别于C的一些概念
1 2 3 4 5 6 7 8 9 10
| #include <iostream> using namespace std;
int main() { cout << "Hello World"; return 0; }
|
头文件并没有什么区别,第二行叫做命名空间。
命名空间:C++中的一个关键字,在C/C++中变量、函数和类都是大量存在且存在于全局变量域中,就会导致命名冲突,命名空间就是为了解决这个问题。
命名冲突:
1 2 3 4 5 6 7 8 9 10 11
| #include<stdio.h> #include<stdlib.h>
int rand = 0; int main() { printf("%d", rand); return 0; }
|
定义命名空间:
1 2 3
| namespace n1{ int rand = 0; }
|
Tip:同一个作用域不能同时出现两个相同的变量,命名空间不影响生命周期。
命名空间的特性:
1.定义变量、函数和类型。
1 2 3 4 5 6 7 8 9 10 11
| namespace n1{ int a; int add(int start, int end){ return start - end; } struct listnode{ int val; struct listnode* next; } }
|
2.命名空间可以嵌套。
1 2 3 4 5 6 7 8 9 10
| namespace n2{ int a; int b; namespace n3{ int c; int d; } }
n2::n3::c
|
3.同一个工程允许存在多个相同名称的命名空间,编译器最后会合到一起。
在C语言中,存在这局部优先的原则,若是我需要使用某一个域的变量,这时候就需要使用到域作用限定符(::)。
1 2 3 4 5 6 7
| int a = 0; int main(){ int a = 1; printf("%d\n", a); printf("%d\n", ::a); return 0; }
|
加上::访问的就是指定域的内容,若前面是空白,那就是全局域,若是某一个命名空间,那就是访问命名空间里的内容;访问命名空间一共有三种方法:
1.加命名空间名称和域作用限定符
1 2 3 4 5 6 7 8 9 10
| namespace n1{ int a; int rand; }
int main(){ printf("%d\n", n1::a); printf("%d\n", n1::rand); return 0; }
|
2.利用using namespace 命名空间名称全部展开
1 2 3 4 5 6 7 8 9 10 11
| namespace n1{ int a; int rand; } using namespace n1;
int main(){ printf("%d\n", a); printf("%d\n", rand); return 0; }
|
Tip:值得注意的是,若是全部展开可能又会出现命名冲突的情况。
当然在使用using namespace时,展开的顺序一定不能颠倒,所以针对某些特定的命名冲突问题,需要单独进行讨论,由于全部展开的这种方式存在一些弊端,所以我们有第三个方法进行展开,那就是部分展开,用到什么展开什么。
3.使用using将命名空间中的成员进行展开
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| namespace n1 { int f = 0; int rand = 0; } using n1::f;
int main() { f += 2; printf("%d\n", f); n1::rand += 2; printf("%d\n", n1::rand); }
|
那么在C++的标准库命名空间就是std。
1 2 3 4 5 6 7
| #include<iostream> using namespace std; int main() { cout << "hello world" << endl; return 0; }
|
若是省去using namespace std,则需要改成以下形式:
1 2 3 4 5 6
| #include<iostream> int main() { std::cout << "hello world" << std::endl; return 0; }
|
在C++中,标准的输入和输出和C中有些许不同,使用标准输出cout,必须包含iostream头文件和std标准空间,为了正确的使用命名空间,规定C++头文件不带.h的后缀。
1 2 3 4 5 6 7 8 9 10 11
| #include <iostream> using namespace std;
int main(){ int a; double b; char c; cin >> a >> b >> c; cout << a << ' ' << b << ' ' << c << endl; return 0; }
|
C++关键字和数据类型

C++的数据类型,大部分和C语言相同,多一个宽字符型。
1
| typedef short int wchar_t;
|
实际上和short int所占的空间是一样的,一些基本类型可以使用一个或者多个类型修饰符进行修饰。
修饰符:signed;unsigned;short;long。
float和double的范围计算:
【详细解说】单精度浮点数float取值范围_float范围-CSDN博客
主要的难点在于偏移量,float是127,double是1023。
C++的类型转换
类型转换分为四类:静态转换、动态转换、常量转换和重新解释转换。
静态转换:将数据从一个类型强行转换为另一种类型,通常用于类型相似之间的转换,且不会进行类型的检查,可能会导致运行错误。
动态转换:将一个基类指针或引用转换为派生类的指针或引用,会进行类型检查,不能转换则会返回空指针和报错。
常量转换:用于将const类型的对象转换为非const的对象,只能转换掉属性,而不能改变其类型。
重新解释转换:将一个类型的值重新解释为另一个数据类型的值,用于不同数据类型的转换。