C++ 语言指南
命名规则以两个下划线或者一个下划线和大写字母开头的名称被保留以一个下划线开头的名称被保留命名空间#include “ custom.h ”包含某个自定义的库,使用双引号外加.h后缀#include <iostream>包含某个标准库, 使用尖括号using namespace std 使用某个标准空间,可以在函数内声明【仅在该函数内有效】,也可以在全局声明数据...
·
命名规则
- 以两个下划线或者一个下划线和大写字母开头的名称被保留
- 以一个下划线开头的名称被保留
命名空间
#include “ custom.h ”
包含某个自定义的库,使用双引号外加.h
后缀#include <iostream>
包含某个标准库, 使用尖括号using namespace std
使用某个标准空间,可以在函数内声明【仅在该函数内有效】,也可以在全局声明
数据
-
把int 类型的数字赋值给布尔的时候,大于0为true,小于等于0为false
-
把布尔赋给int类型的时候,true为1,false为0
-
int short long long
-
float double long double
-
char 可以直接赋给int,int也可以赋给char
-
C++的取模运算必须都是整数类型,不支持浮点数。
-
强制类型转换
int( )
(int) k
-
auto关键字可以自动推断类型
auto a = 12;
.缺点,浮点值默认类型是double类型,可能自动推导可预期不符。 -
数组声明
int num[] = {1,2,3}; int num2[3] = {1,2};
在C++11中可以去掉等号int num3[]{1,2,3}
数组的声明和多数据初始化必须在同一行,否则只能单个赋值 -
在C++中有两种风格的字符串,一种是C语言风格
- 用字符数组代替字符串
char name[] = { 'a','p','p','l','e' ,'\0'}
但是这种需要把字符串的每个字符列出来,还需要加上空字符‘\0’
- 用字符串直接赋值
char[] name = "apple"
会自动加上空字符和填充数组 strlen(name)
会获取字符串的长度,不包括‘\0’
空字符,但是数组的长度是包括空字符的- 可以多行字符串,自动进行拼接,
char name[] = " my name is apple" "well , you do"
- 用字符数组代替字符串
-
另一种是C++的类库风格,使用string需要引入
#inclue<string>
的库- 字符串可以相加
string str1,str2; str1+=str2
- 获取字符串的长度
str1.size()
- 获取用户输入
getline(cin, str1);
- 字符串可以相加
-
cin的问题,cin是以空格符,制表符, 回车结束的,但是输入的字符串中可能包含空格符 改用
cin.getline()
获取一行
cout << "请输入名字";
char name[30];
cin>>name;
cout << "请输入你的食物";
char food[12];
cin >> food;
cout << name << "喜欢吃" << food;
指针
- 指针在使用之前必须初始化
- 指针可以赋空值,有三种方法
int* c = 0; float* d = NULL; float* k = nullptr;
- 空类型指针可以接受任意类型的数据的指针,但是取值时需要强转
int a = 10; void* ptr = &a; cout<< *(int*)ptr
最后输出10 - 可以为基础类型分配空间,
int* p = new int; *p = 10; delete p;
数组
- 创建数组的三种方式
- 普通方式
int num[] = {1,2,3}
,数组和数组之间无法赋值 - 通过指针分配空间方式
int* numPtr = new int[3]{1,2,3}; delete[] numPtr;
- 但凡是通过
new
分配的空间都需要在使用完毕之后通过delete
释放 - 通过模板类, 引用
#include <array>
,第一个参数是类型,第二个参数数组长度,array<int, 3> numModel = {1,2,3}
- 模板类创建的数组之间可以互相赋值
array<int, 4> num5 = { 1,6,7,8 }; array<int, 4> num6 = { 0,7,9,4 }; num5 = num6;
- 普通方式
遍历数组的方式
array<int, 3> num = { 1,2,3 };
for(int& i:num)
{
i = 11; //支持读写
}
for (int i : num) { //只支持读取
cout << i;
}
函数和数组
- 存在局部静态变量,和局部变量的区别在于,一个在程序终止的时候销毁,一个在函数运行完的时候销毁
- 函数需要先声明后使用。【这里我比较疑惑,因为预先声明是很麻烦的,完全可以用预编译来解决】
引用类型
int&
定义引用类型,两个变量指向同一块内存区域,相当于获取某块内存区域的地址然后赋给对应指针int a = 10; int& b =a;
int&
类型可以赋值给int&
,两者指向同一块内存区域,也可以赋值给int
类型,但只是把数据拷贝一份赋给int
数据而已- 指针类型的引用
const
const int * p
可以修改指针p的值,但是不能修改p指向内存区域的值int* p
因为p可以修改其内存区域的值,因此不能这样const int b; p = &b;
会报错int * const
不可以修改指针p的值,但是可以修改p指向内存区域的值,因此不能这样const int b; p = &b;
会报错const int& b
可以接收int
和const int
的值,b的值无法改变int& b
只可以接受int
的值
默认参数
- 在C++中也存在默认参数,需要在函数声明中标明
int Add(int a, int b =100);
int Add(int a, int b) {
return a + b;
}
内联函数
- 将对应的函数调用替换为函数的代码,为了加快运行速度
- 但是这样会增大内存
- 在函数声明或函数定义前面加上关键字
inline
- 注意内联函数不能递归;
常量表达式
ConstExpr int AddTen(int a){
return a+10;
}
- 默认是内联函数
- 可以递归, 参数和返回类型都必须是字面值类型。
- 如果传入的是常量,会在编译期自动计算出结果,如果是变量,会在运行期计算出结果
函数指针
- 函数指针指向的是函数而非对象
- 想要声明一个函数指针,只需要用指针替换函数名即可
- 可以用typedef定义一个函数指针类型
- 可以返回一个函数指针,也可以当做形参传递
bool lengthCompare(const string&, const string&); //函数
bool (*pf)(const string&, const string&); //函数指针
int AddPf(int a, int b, int (*sd)(int, int)) {
return sd(a, b);
}
typedef int(*len) (int, int);
int main()
{
len ptr = Add;
int result = AddPf(12, 23, ptr);
cout << result;
}
申请内存
- 申请内存不一定能成功
- 所以在申请之后要判断是否为null
- 此外还要将对应指针赋值为null 这样就可以避免重复释放
int*p = new int[100];
if(p==Null){
//内存分配失败
}
p = Null;
定义类
- 类内定义
- 在类定义内的成员函数会隐式的编译成内联函数;
- 内联函数
inline
是编译建议,编译器会自己根据函数的复杂程度自己决定是否内联 - 类外定义分为同文件类外定义和分文件类外定义【有经验都是分开】
- 同文件即将定义和函数写在一个cpp中,函数体在外,需要标注类名前缀
- 分文件定义即将定义写在
.h
头文件中,将函数体写在另外的.cpp
文件中,.cpp
中需要引用其头文件,同样需要标注前缀
- 初始化列表
Student() :age(35),name("ka"){}
- 初始化列表先于构造函数执行
- 初始化列表只能用于构造函数
- 初始化列表可以同时初始化多个数据成员
- 必要性,这样就可以修改const值
class Circle
{
private:
const double dPi;
public:
void Print(Student& stu);
Circle():dPi(3.14) { }
};
- 拷贝构造函数 当一个对象发生拷贝的时候,该函数就会被调用,编译器会提供默认实现
Student( const Student& copy)
.void PrintStudent(Student stu)
作为参数传递的时候发生拷贝Student s1; Student s2(s1); Student s3 = s1;
发生拷贝
void Test(Student& s1) {}
int main()
{
//Student* s1 = new Student("贾亚杰", 45, 12, 23, 45, 20141026);
Student s1("不流人", 46, 12, 23, 45, 20141027);
Student s2 = s1;
Student s3(s1);
Test(s1);
}
- 分文件 类外定义
- 分为两个部分,类的声明
.h
部分和函数实现部分.cpp
.h
部分
#pragma once
#include <string>
using namespace std;
class Student
{
private:
string name;
float mathScore;
float chineseScore;
float englishScore;
float physicsScore;
int id;
public:
Student(string nameM, float mathScoreM, float chineseScoreM, float englishScoreM, float physicsScoreM, int idM);
~Student();
float GetAverage();
string GetName();
float GetSumScore();
bool CompareScore(Student& other);
};
.cpp
部分
#include "Student.h"
#include <iostream>
Student::Student(string nameM, float mathScoreM, float chineseScoreM, float englishScoreM, float physicsScoreM, int idM)
{
name = nameM;
mathScore = mathScoreM;
chineseScore = chineseScoreM;
englishScore = englishScoreM;
physicsScore = physicsScoreM;
id = idM;
}
Student::~Student()
{
cout << "销毁" << this->name;
}
float Student::GetAverage()
{
return this->GetSumScore() / 4;
}
string Student::GetName()
{
return name;
}
float Student::GetSumScore()
{
return mathScore + chineseScore + englishScore + physicsScore;
}
bool Student::CompareScore(Student& other)
{
return this->GetAverage() > other.GetAverage();
}
实例化对象
- 从栈中实例化对象,当函数运行结束后就会自动释放
- 从堆中实例化对象通过new,当不再使用的时候要用delete进行释放
- 析构函数
- 在对象被销毁时候被调用的函数,可以在这个函数中做一些释放内存的操作
- 编译器会自动添加
~Student(){ }
- 通过拷贝构造函数产生的对象在释放的时候也会调用
Student* s1 = new Student("贾亚杰", 45, 12, 23, 45, 20141026);
Student s2("不流人", 46, 12, 23, 45, 20141027);
cout << s1->GetName() << s1->GetAverage();
cout << s2.GetName() << s2.GetAverage();
cout << s1->GetName() << "比" << s2.GetName() << "大" << s1->CompareScore(s2);
delete s1; //当s1被删除的时候,析构函数就会被调用
s1 = NULL;
- 创建类的对象数组时候,这个类必须有默认构造函数,因为初始化对象的时候,会首先使用默认构造函数创建数组元素
Student stus[5] = {};
stus[0] = Student("贾杰", 45, 12, 23, 45, 20141026);
stus[1] = Student("贾发", 45, 12, 23, 45, 20141027);
stus[2] = Student("贾亚", 45, 12, 23, 45, 20141028);
for (int i = 0; i < size(stus); i++)
{
cout << stus[i].GetName();
}
对象成员
class Line
{
Point end;
Point start;
public:
Line(float sx, float sy, float ex, float ey);
};
int main(){
Line* line = new Line(1, 2, 3, 4);
delete line;
//创建点34创建点12创建line销毁line12被销毁34被销毁 其顺序
}
- 在实例化的时候首先实例化
end
,其次实例化start
,最后实例化Line
- 对象成员指针
#include "Line.h"
#include <iostream>
using namespace std;
Line::Line(float sx, float sy, float ex, float ey)
{
start = new Point(sx, sy);
end = new Point(ex, ey);
cout << "创建line" << endl;
}
Line::~Line()
{
cout << "销毁line" << endl;
delete start;
start = NULL;
delete end;
end = NULL;
}
- 常对象成员
- 用const修饰的对象成员,不能改变,只能在初始化列表中初始化
- 常成员函数
- 用const修饰的成员函数,不允许改变对象自身的值,因为const被隐士的加到
this
指针前面
void Student::GetName(){ }
//在编译时会隐式的添加 this指针
void GetName(Student* this)
//用const修饰的成员函数
void Student::GetName() const { }
//在编译时会隐式的添加
void GetName(const Student* this){}
//因为this指针被const修改,所以无法通过this来修改数据
void Student::GetName(){ } //两者互为重载,那么如何辨别你调用的是哪一个函数呢
void Student::GetName() const { }
const Student good("007"); //通过const修饰的对象称为常对象
good.GetName(); //通过常对象来调用的函数即为常成员函数
更多推荐
已为社区贡献3条内容
所有评论(0)