0. 库
<>用于编译器的include路径,“”用于所有
c的标准库有.h扩展名,c++的标准库没有
1.指针 * &
C语言有两种表示字符串的方法,一种是普通的字符数组,另一种是字符串常量,它们在内存中的存储位置不同,使得字符数组可以读取和修改,而字符串常量只能读取不能修改。
直接使用一个指针指向字符串,例如
char *str = "http://c.biancheng.net";
即是字符串常量。
常量指针与指针常量:常量指针说的是不能通过这个指针改变变量的值,但是这并不是意味着指针本身不能改变,常量指针可以指向其他的地址;指针常量是指指针本身是个常量,不能在指向其他的地址。指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向改地址的指针来修改。如果我们将星号读作‘指针’,将const读作‘常量’的话,内容正好符合。int const * n;是常量指针,int *const n;是指针常量。
2.引用 int&
3.静态变量 static
如果我们希望全局变量仅限于在本源文件中使用,在其他源文件中不能引用,也就是说限制其作用域只在定义该变量的源文件内有效,而在同一源程序的其他源文件中不能使用。这时,就可以通过在全局变量之前加上关键字 static 来实现,使全局变量被定义成为一个静态全局变量。这样就可以避免在其他源文件中引起的错误。也就起到了对其他源文件进行隐藏与隔离错误的作用,有利于模块化程序设计。
当将局部变量声明为静态局部变量的时候,也就改变了局部变量的存储位置,即从原来的栈中存放改为静态存储区存放。这让它看起来很像全局变量,其实静态局部变量与全局变量的主要区别就在于可见性,静态局部变量只在其被声明的代码块中是可见的。静态局部变量一般的使用场景,如下所示:
- 需要保留函数上一次调用结束时的值。
- 如果初始化后,变量只会被引用而不会改变其值,则这时用静态局部变量比较方便,以免每次调用时重新赋值。
4.枚举 enums
enum Example
{
A = 0, B, C
};
5.构建 constructor
class Entity #construct初始化
{
public:
float X,Y;
Entity()
{
X = 0.0f;
Y = 0.0f;
}
};
class Entity #初始化成员列表
{
private:
std::string m_Name;
int m_Score;
public:
Entity()
: m_Name("Unknown"), m_Score(0)
{
}
Entity(const std::string& name)
: m_Name(name)
{
}
}
6.解构 destroctor
~Entity()
{
}
7.继承 inherit
class Player : public Entity
{
};
8.虚函数 virtual override
class Entity
{
public:
virtual std::string GetName() { return "Entity"; }
};
class Player : public Entity
{
std::string GetName() override { return "Peter"; }
}
9.纯虚函数
class Entity
{
public:
virtual std::string GetName() = 0;
};
#include <iostream>
using namespace std;
//线
class Line{
public:
Line(float len);
virtual float area() = 0;
virtual float volume() = 0;
protected:
float m_len;
};
Line::Line(float len): m_len(len){ }
//矩形
class Rec: public Line{
public:
Rec(float len, float width);
float area();
protected:
float m_width;
};
Rec::Rec(float len, float width): Line(len), m_width(width){ }
float Rec::area(){ return m_len * m_width; }
//长方体
class Cuboid: public Rec{
public:
Cuboid(float len, float width, float height);
float area();
float volume();
protected:
float m_height;
};
Cuboid::Cuboid(float len, float width, float height): Rec(len, width), m_height(height){ }
float Cuboid::area(){ return 2 * ( m_len*m_width + m_len*m_height + m_width*m_height); }
float Cuboid::volume(){ return m_len * m_width * m_height; }
//正方体
class Cube: public Cuboid{
public:
Cube(float len);
float area();
float volume();
};
Cube::Cube(float len): Cuboid(len, len, len){ }
float Cube::area(){ return 6 * m_len * m_len; }
float Cube::volume(){ return m_len * m_len * m_len; }
int main(){
Line *p = new Cuboid(10, 20, 30);
cout<<"The area of Cuboid is "<<p->area()<<endl;
cout<<"The volume of Cuboid is "<<p->volume()<<endl;
p = new Cube(15);
cout<<"The area of Cube is "<<p->area()<<endl;
cout<<"The volume of Cube is "<<p->volume()<<endl;
return 0;
}
在 Rec 类中,实现了 area() 函数;所谓实现,就是定义了纯虚函数的函数体。但这时 Rec 仍不能被实例化,因为它没有实现继承来的 volume() 函数,volume() 仍然是纯虚函数,所以 Rec 也仍然是抽象类。
直到 Cuboid 类,才实现了 volume() 函数,才是一个完整的类,才可以被实例化。
可以发现,Line 类表示“线”,没有面积和体积,但它仍然定义了 area() 和 volume() 两个纯虚函数。这样的用意很明显:Line 类不需要被实例化,但是它为派生类提供了“约束条件”,派生类必须要实现这两个函数,完成计算面积和体积的功能,否则就不能实例化。
在实际开发中,你可以定义一个抽象基类,只完成部分功能,未完成的功能交给派生类去实现(谁派生谁实现)。这部分未完成的功能,往往是基类不需要的,或者在基类中无法实现的。虽然抽象基类没有完成,但是却强制要求派生类完成,这就是抽象基类的“霸王条款”。
抽象基类除了约束派生类的功能,还可以实现多态。请注意第 51 行代码,指针 p 的类型是 Line,但是它却可以访问派生类中的 area() 和 volume() 函数,正是由于在 Line 类中将这两个函数定义为纯虚函数;如果不这样做,51 行后面的代码都是错误的。我想,这或许才是C++提供纯虚函数的主要目的。
10.访问控制 public protected private
11.堆和栈数组
总结: (1)heap是堆,stack是栈; (2)stack的空间由操作系统自动分配/释放,heap上的空间手动分配/释放; (3)stack空间有限,heap是很大的自由内存区; (4)C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。 程序在编译对变量和函数分配内存都在栈上进行,且内存运行过程中函数调用时参数的传递在栈上进行。
12.字符串
# include <string>
void PrintString(const std::string& string)
{
std::cout << string << std::endl;
}
int main()
{
std::string name = std::string("hello") + "world";
std::cout << name << std:endl;
}
13.const
C语言中const关键字的用法_xingjiarong的博客-CSDN博客
class Entity
{
private:
int m_X, m_Y;
mutable int var;
public:
int GetX() const
{
m_X = 2; #这段会报错,因为该方法是恒定的,意味着不能改变任何成员变量
var = 2; #这段不会报错,因为有关键字mutable
return m_X;
}
}
const int MAX_AGE = 90;
const int* a = new int;
*a = 2; #这段会报错,因为指针是一个常量指针,里面的值不能更改
a = (int*)&MAX_AGE;
int* const a = new int;
*a = 2;
a = (int*)&MAX_AGE; #这段会报错,因为指针所指向的地址是恒定的
14.三元运算符
int main()
{
if (s_Level > 5)
s_Speed = 10;
else
s_Speed = 5;
s_Speed = s_Level > 5 ? 10 : 5;
}
15.两种方法创建实例化的对象
int main()
{
Entity entity("Pete"); #创建的是栈类型的实例化对象,在for循环或者其它函数运行结束后就会摧毁
}
int main()
{
Entity* entity = new Entity("Pete");
int* b = new int[20];
delete entity; #创建的是堆类型的实例化对象,处理时间上会变慢,但是除非delete不然一直存在
delete[] b;
}
16.隐性转换和显式转换
隐性转换一次只能自动转换一回
显式转换要用关键字约束 explicit
class Entity
{
private:
std::string m_Name;
int m_Age;
public:
explicit Entity(int age)
: m_Name("Unknown"), m_Age(age) {}
};
int main()
{
Entity a = "Pete";
Entity b = 22; #如果没有explicit关键字,这个是可以的,因为它启用隐性转换自动把int型转换为实体类型
Entity b(22);
Entity b = Entity(22); #这是两种更常用的
}
17. 函数重载和 运算符重载 operator
在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。
#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;
}
#include <iostream>
#include <string>
struct Vector2
{
float x, y;
Vector2(float x, float y)
: x(x), y(y) {}
Vector2 Add(const Vector2& other) const
{
return Vector2(x + other.x, y + other.y);
}
Vector2 operator+(const Vector2& other) const
{
return Add(other);
}
Vector2 Multiply(const Vector2& other) const
{
return Vector2(x * other.x, y * other.y);
}
Vector2 operator*(const Vector2& other) const
{
return Multiply(other);
}
};
int main()
{
Vector2 position(4.4f, 4.4f);
Vector2 speed(0.5f, 1.5f);
Vector2 powerup(1.1f, 1.1f);
Vector2 result1 = position.Add(speed.Multiply(powerup));
Vector2 result2 = position + speed * powerup;
std::cin.get();
}
18.命名空间 namespace
在命名时,变量名有可能会和你调用的库中的变量名冲突,为了解决这个问题,C++提出了命名空间这个概念,即对标识符的名称进行本地化,以此解决命名污染的问题,其作用就是定义一个新的作用域(scope)。经常会用到命名空间分解运算符(作用域符) "::"。在变量名冲突时,程序会遵循局部优先原则
作用域符:c++入门学习篇(1)之::作用域符解析 - 知乎
namespace关键字用来将变量定义在自己创建的命名空间,以避免被污染访问命名空间中的变量,需要使用作用域分解运算符命名空间并且只能定义在全局,其中定义的变量为全局变量。
using namespace可以将需要的命名空间彻底释放出来,就不需要在每次需要使用命名空间里的变量或者函数时在调用一次了。所以当我们在写成熟的代码的时候,一般不建议将标准命名空间(std)全部打开,而是需要用库里的什么就打开什么。
参考: