c++是中级语言,具有面向对象开发的四大特性:封装、抽象、继承、多态;由三部分组成:核心语言、大量函数、操作数据结构的模板库STL
1.基础
1.1 数据类型
- 基本7种:bool/char/int/float/double/void/wchar_t(宽字符,typedef short int wchar_t),可以用修饰符signed/unsigned/long/short修饰
- typedef 别名
- enum(enumeration)枚举类型
//定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 "blue";
//blue 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1,但 red 的值依然为 0
enum color {red, green=5, blue } c;
c = blue;
复制代码
1.2 变量
- 声明与定义:变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译;可以使用 extern 关键字在任何地方声明一个变量,在 C++ 程序中可多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次
- 局部与全局:在函数或一个代码块内部声明的变量,称为局部变量,系统不会对其初始化;在所有函数外部定义的变量(通常是在程序的头部),称为全局变量,会被系统初始化为0,’\0′,NULL等
1.3 常量
- 范围:包括整数、浮点数、布尔、字符、字符串常量
- 定义:#define或者const关键字
1.4 存储类
存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期
- auto:根据初始化表达式自动推断被声明的变量的类型
auto f=3.14; //double
auto s("hello"); //const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型
复制代码
- register:用于定义存储在寄存器中而不是 RAM 中的局部变量,寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 ‘register’ 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。
- static:使用 static 修饰局部变量可以在函数调用之间保持局部变量的值,也就是有点全局的意思了,函数内部的变量在程序的生命周期内保持局部变量的存在。
- extern:用来在另一个文件中声明一个全局变量或函数,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
//main.cpp
#include <iostream>
int count ;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
//support.cpp
#include <iostream>
extern int count;
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
//编译执行后Count is 5
复制代码
- mutable
- thread_local
1.5 函数
每个 C++ 程序都至少有一个函数,即主函数 main()
- 函数声明:函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义
int max(int num1, int num2);
int max(int, int);//参数的名称并不重要,只有参数的类型是必需的
复制代码
- 调用函数
- 函数参数:当调用函数时,有三种向函数传递参数的方式:传值、指针、引用;后面两种修改形式参数都会影响实际参数
- lambda函数与表达式
1.6 指针
int var1;
cout << &var1 << endl;//查看地址
复制代码
指针是一个变量,其值为另一个变量的地址
int *ip; /* 一个整型的指针 */
复制代码
1.7 引用
引用变量是一个别名,与指针相比有3个不同,总的来说是不能空、不可改、必须初始化:(1)不存在空引用。引用必须连接到一块合法的内存(2)一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。(3)引用必须在创建时被初始化。指针可以在任何时间被初始化。
// 声明简单的变量
int i;
double d;
// 声明引用变量
int& r = i;
double& s = d;
复制代码
2.命名空间
命名空间,是一种将程序库名称封装起来的方法,它就像在各个程序库中立起一道道围墙。比如:
// one.h
namespace one
{
char func(char);
class String { ... };
}
// somelib.h
namespace SomeLib
{
class String { ... };
}
复制代码
现在就算在同一个程序中使用String类也不会发生冲突了,因为他们分别变成了:one::String()以及Somelib::String()
C++标准库定义了命名空间:std,其中包含容器vector等
3.类和对象
3.1 类
举例如下,其余具体的没看
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
// 成员函数声明
double get(void);
void set( double len, double bre, double hei );
};
// 成员函数定义
double Box::get(void)
{
return length * breadth * height;
}
void Box::set( double len, double bre, double hei)
{
length = len;
breadth = bre;
height = hei;
}
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 用于存储体积
// box 1 详述
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 详述
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// box 1 的体积
volume = Box1.height * Box1.length * Box1.breadth;
cout << "Box1 的体积:" << volume <<endl;
// box 2 的体积
volume = Box2.height * Box2.length * Box2.breadth;
cout << "Box2 的体积:" << volume <<endl;
// box 3 详述
Box3.set(16.0, 8.0, 12.0);
volume = Box3.get();
cout << "Box3 的体积:" << volume <<endl;
return 0;
}
复制代码
3.2 继承
一个派生类可以从多个基类继承数据和函数;派生类不能访问private,外部的类不能访问private和protected
// 派生类
class Rectangle: public Shape//如果未使用访问修饰符,则默认为 private;一般都用public当继承类型
{
public:
int getArea()
{
return (width * height);
}
};
复制代码
不同继承类型遵循如下规则:
- public:基类的public和protected属性不变,private不能直接被访问
- protected:基类的public和protected都变成派生类的protected
- private:基类的public和protected都变成派生类的private
4.重载运算符和重载函数
5.多态
- 多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的.
- 虚函数:是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。
举例
#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() 被编译器设置为基类中的版本,这就是所谓的静态多态,修改如下*/
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
/*Rectangle class area Triangle class area,由于 tri 和 rec 类的对象的地址存储在 *shape 中,所以会调用各自的 area() 函数*/
复制代码
6.数据抽象和封装
6.1 抽象
只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节
6.2 封装
封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念,这样能避免受到外界的干扰和误用,从而确保了安全。数据封装是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。
举例
#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);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
复制代码
7.抽象类
接口描述了类的行为和功能,而不需要完成类的特定实现。C++ 接口是使用抽象类来实现的,如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 “= 0” 来指定的
class Box
{
public:
// 纯虚函数
virtual double getVolume() = 0;
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
复制代码
设计抽象类(通常称为 ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。抽象类不能被用于实例化对象,它只能作为接口使用。如果试图实例化一个抽象类的对象,会导致编译错误。
因此,如果一个 ABC 的子类需要被实例化,则必须实现每个虚函数.