介绍一些个人对C++语法的学习,正在慢慢完善
#include //引用输入输出流
/*引用注释*/
using namespace std; //使用命名空间std
int main() { //主函数
cin >> a;
std :: cout << "int类型占用的空间:" << sizeof(int) << std::endl;
return 0;
}
大体上与C语言类似,有以下几个区别:
- < iostream >与C不同的是C++的库可以不带.h
- std命名空间,当不使用.h文件时类和函数都在std里声明,声明了std就可以去掉std::
- cin、cout分别为输入输出流对象
- <<、>>分别为插入运算符与提取运算符
- endl为末尾结束换行,还具备了刷新数据打印缓冲区的功能
- ::类标识符,表示后面属于前面,类似C结构体调用
- return执行完后跳出函数
数据类型 | 声明 |
---|---|
整数类型 | int |
单精度浮点数 | float |
双精度浮点数 | double |
字符 | char |
字符串 | string |
布尔 | bool |
常量其实和变量没有多大区别,有名字,占据存储空间,可以是任何的基本类型,但只有一点不同,常量的值不允许变更。C++中的常量的声明需要使用const 关键字,而python中的常量默认约定是全大写表示。
int main(){
const double pi {3.1415926};
pi = 2.5 ; //将会出现编译错误
return 0 ;
}
1.可以包含字母 、 数字 和下划线
2.必须以字母或者下划线开始
int age ; //未初始化
int age1 = 21 ; // C 方式初始化
int age2 (21); //构造方法初始化
int age3 {21} ; //c++ 11标准开始的方式
基本运算符有+,-,*,/,%,运算中有一个数为float型数据,则运算结果为double型
强制类型转换运算符,格式:(类型名)表达式
(int)(x+y)
赋值运算符就是=
C++提供逗号运算符,表示表达式按顺序连接起来
a = 3*5,a*4
任何编程语言都要输入和输出,python的输入输出是input 和 print , C语言的输入输出是 scanf 和 printf ,而C++的相对要复 杂些,它使用 std::cin 和 std::cout 来操作输入输出 。 C++的输入输出,需要导入iostream库
std::cout << "请输入您的年龄:"<<std::endl;
endl 来表示输出结束,它除了含有换行的功能之外,还具备了刷新数据打印缓冲区的功能
//由于没有换行,两个单词会出于同一行。
std::cout << "Hello"; std::cout << " world";
//兼备换行的输出
std::cout << "Hello" << std::endl; std::cout << " world" << std::endl;
//可以连续输出打印,这其实是一种叫做:链式调用的手法
std::cout << "Hello " << " World" << " , I love C++!" << std::endl;
Operator | 符号 |
---|---|
等于 | == |
不等于 | != |
大于 | > |
小于 | < |
大于或等于 | >= |
小于或等于 | <= |
Operator | 符号 |
---|---|
与 | && |
或 | || |
非 | ! |
char result = score > 90 ? 'A' : 'B';
if( score > 90){
std::cout << "可以休息一天" << std::endl; }
else if(score > 80){
std::cout << "可以休息半天" << std::endl;
}else{
std::cout << "乖乖去写作业" << std::endl;
}
switch (level){
case 'A':
std::cout << "优秀" << std::endl;
break;
case 'B':
std::cout << "良好" << std::endl;
break;
}
int count = 0 ;
while(count < 10){
std::cout << "红灯还在亮着..." << std::endl;
//单位是毫秒
Sleep(1000);
count++;
}
int count = 0 ;
do{
std::cout << "红灯还在亮着..." << std::endl;
//单位是毫秒
Sleep(1000);
count++;
}while(count < 10);
for(int cout = 0 ; cout < 10 ; cout ++)
std::cout << "红灯还在亮着..." << std::endl;
continue:跳出本次循环,进入下一个循环
break:跳出循环体
int number = 1 ;
while(number <= 20){
if(number == 16){
break;
}
//满足条件,表示当前的number是奇数。
if(number % 2 != 0 ){
continue;
}
std::cout << "当前打印的数字是: " << number << std::endl;
number++;
}
void say_hello(){
cout << "你好 " << endl;
}
void say_hello(string name){
cout << "你好 "<< name << endl;
}
string say_hello(){
return "你好 张三";
}
string say_hello(string name){
return "你好 "+ name;
}
#include
using namespace std;
//函数声明,如果函数调用在函数定义之前,则需要进行函数声明,从而初始化函数
int add (int a , int b);
int main(){
cout << add(1 ,2)<< endl; return 0 ;
}
//函数定义 ,函数的真正实现。
int add(int a , int b){
return a + b ;
}
#include
using namespace std;
void scale_number(int num);
int main(){
int number{1000};
scale_number(number);
//打印number 1000
cout << number << endl;
return 0 ;
}
void scale_number(int num){
if(num > 100) num = 100;
}
#include
using namespace std;
void scale_number(int &num);
int main(){
int number{1000};
scale_number(number);
//打印number100
cout << number << endl;
return 0 ;
}
void scale_number(int &num){
if(num > 100) num = 100;
}
数组是一系列相同类型的元素,放置在连续的内存位置,数组中的元素都可以通过索引来单独操作它们。 若查看某个变量存储地址可以使用 取地址符 &
//1. 声明后再初始化
int scores [5];
scores[0] = 11; scores[1] = 22; scores[2] = 33; scores[3] = 44; scores[4] = 55;
//2. 声明并初始化
int scores2 [5]{100,89,95,70,80};
//3. 自动推算数组大小
int scores3[]{22,33,44,55,66};
4.2.1 获取数组中的某个元素
std::cout<<"数组的第一个元素是: "<< scores[0]<<std::endl;
4.2.2 遍历数组
int main(){
//定义数组
int scores[]{100,95,97,88,85,80,75};
//直接指定数组
for(int i = 0; i < 7; i++){
std::cout << scores[i] << std::endl;
}
//手动计算数组长度
int length = sizeof(scores) / sizeof(int);
for(int i = 0 ; i < length; i++){
std::cout << scores[i] << std::endl;
}
//c++11 提供的for循环
for(int score : scores){
std::cout <<score << std::endl;
}
return 0 ;
}
C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。需要引入 #include< string>,由于string类声明在命名空间 std ,所以在使用的首要注意 命名空间的联合使用 。
//引入string库
#include
using namespace std;
int mian(){
string s1;
string s2 {"北京"};
string s3{s2};
string s4 = "你好";
s1 = s3;
return 0 ;
}
Vector其实很大程度上和数组一样,只是数组是固定长度,而vector是不定长度(动态增长)。vector 在C++STL(标准模板库)中的一个容器,可以看成是对容器的一种扩展。
#include
using namespace std;
int main(){
vector <char> vowels2(5); //声明一个初始大小为5的char类型
vector <char> vowels {'a' , 'e' , 'i' , 'o' ,'u'};
return 0;
}
#include
#include
using namespace std;
int main(){
vector<int> test_score {100,90,85};
//以数组方式
cout << "第一个成绩是: " <<test_score[0] << endl;
cout << "第二个成绩是: " <<test_score[1] << endl;
cout << "第三个成绩是: " <<test_score[2] << endl;
cout << "第三个成绩是: " <<test_score[3] << endl;//不会检查越界
//vector的语法
cout << "第一个成绩是: " <<test_score.at(0) << endl;
cout << "第二个成绩是: " <<test_score.at(1) << endl;
cout << "第三个成绩是: " <<test_score.at(2) << endl;
cout << "第三个成绩是: " <<test_score.at(3) << endl; // 抛出越界异常
return 0 ;
}
①修改vector中的元素
vector<int> test_score {100,90,85};
test_score.at(0) = 73;
②往vector中追加元素
vector<int> test_score {100,90,85};
test_score.push_back(80); // 100 , 90 , 85 , 80
③遍历vector
//使用下标遍历
vector<int> scores{ 100 ,95 ,88 ,80 ,75};
for (int i = 0; i < scores.size(); ++i) {
cout << scores[i] << endl;
}
//基于范围for遍历
vector<int> scores2{ 100 ,95 ,88 ,80 ,75};
for(int score : scores2){
cout << score << endl;
}
指针其实就是一个变量,不过它的值是一个内存地址 , 这个地址可以是变量或者一个函数的地址
#include
int main(){
//初始化指针
int a = 3 ;
int *p0 = &a;
//p0是一个指针,指向的是一个int类型的数据,这个 数据是3.
//空指针,没有指向任何地方
int *p1 = nullptr;
int *p2 = NULL;
int *p3 = 0 ;
return 0 ;
}
#include
int main(){
int age = 88;
int *p = &age;
//函数执行结束, age所占用空间会自动回收
return 0 ;
}
#include
int main(){
//使用new的方式在堆内存中开辟空间。
int *p = new int(88);
//需要手动配合delete 删除
delete p ;
return 0 ;
}
函数的参数,除了传递普通的变量,引用之外,还可以把指针当成参数来传递
#include
using namespace std;
//函数原型
void double_data(int *int_ptr);
int main(){
int value{10};
//修改前,输出为10
cout << value << endl;
//在函数内部修改value的值
double_data(&value);
//修改后,输出为20
cout << value << endl;
}
void double_data(int *int_ptr){
*int_ptr *=2;
}
函数平常除了返回标准的数值之外,其实也可以返回指针。
#include
using namespace std;
//返回两个参数中的最大者
int *calc_largest(int *ptr1 , int *ptr2);
int main(){
int a = 100;
int b = 200;
//使用指针指向最大数值
int *largest_ptr = calc_largest(&a , &b);
//输出:200
cout << *largest_ptr << endl ;
return 0 ;
}
int *calc_largest(int *ptr1 , int *ptr2){
//解引用获取到数据后比较 :
if(*ptr1 > *ptr2){
return ptr1;
}else{
return ptr2;
}
}
引用,顾名思义是某一个变量或对象的别名,对引用的操作与对其所绑定的变量或对象的操作完全等价。引用在使用时,有几个要注意的地方:
- &不是求地址运算符,而是起到标志作用
- 引用的类型和绑定的变量类型必须相同 (指针也一样) 3. 声明引用的同时,必须对其进行初始化,否则报错。
- 引用不是定义一个新的变量或对象,因此内存不会为引用开辟新的空间存储这个引用
- 不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
int main(){
//语法:类型 &引用名=目标变量名;
int a = 3 ;
int &b = a ;
//此时a也会变成33
b = 33;
vector<string> names {"张三" , "李四" ,"王五"};
// 这仅仅是改变了str的值,并不会改变vector里面的元素
for(auto str:names){ str = "赵六"; }
//此处不会产生新的变量,所以修改的都是vector里面的元素
for(auto &str : names){ str = "赵六" ; }
return 0 ;
}
结构体与数组的区别就是,结构体可以包含不同类型的数据项
struct Student //用struct来声明结构体
{int num;
char name[20];
char sex;
int age;
float score;
}student1 = {10001,"zhang Xin",'F',20,18}; //初始化
Student student2 = {10002,"Wang Li",'F',20,68};
结构体指针3种形式等价:
①结构体变量.成员名。如stu.num
②(*p).成员名。如(*p).num
③p -> 成员名。如p -> num
如果变量的值确定,则可以定义枚举类型
enum weekday //用enum申明枚举类型
{
sun,mon,tue,wed,thu,fri,sat
};
typedef float REAL; //用REAL代表float类型
C++用new和delete取代malloc与free来动态分配和撤销存储空间
p = new Student; //用new开辟一个存放Student型数据的空间,地址赋给p
delete p;
对象是一类东西, 而类是构成对象的一个基石,从而描述对象的特征
#include
#include
using namespace std;
class student{
private:
string name; // 姓名
int age; //年龄
public:
void read(){
std::cout << "学生在看书" << std::endl;
}
};
用这种方法创建的对象,内存分配到栈里(Stack)。 直接使用 来访问成员。当程序对象所在的代码块结束运行(方法运行结束),对象会自动删除,内存自动释放。
#include
using namespace std;
class student{
public:
void run(){
cout << "学生在跑步" << endl;
}
};
int main(){
//对象存储于栈内存中
student stu1 ;
stu1.run();
return 0 ;
}
这种方法创建的对象,内存分配到堆里(Heap)。一般使用* 或者 - >调用对象的方法。箭头操作符”->"将解引用(dereferencing* )和成员使用(member access.)结合起来
#include
#include
using namespace std;
class student{
public:
void run(){
cout << "学生在跑步" << endl;
}
};
int main(){
//对象存储于栈内存中 new 关键字是在堆中申请内存
student *s3 = new student;
s3->run();
//采用解引用的方式之后,再进行成员访问
(*s3).run();
return 0 ;
}
c++ 对于成员的访问有三种访问操作符 public | private | protected , 默认情况下是private
- public : 公开权限,任何位置都可以访问
- private : 私有权限,只能自己内部访问及其友元类和函数
- protected : 受保护权限,只能在自己内部、子类及友元类和函数访问
#include
#include
using namespace std;
class student{
private: //表示下面的成员为私有
string name; // 姓名
int age; //年龄
public: //表示下面的函数为公开
void run(){} }
;
int main(){
student stu ;
stu.name = "张三" ; // 禁止访问 , 会报错
stu.run(); //允许访问
return 0 ;
}
- 成员函数可以在类里面直接实现,也可以在类的外部实现
- 可以在类的外部实现成员函数 , 但是需要使用类名::函数名来标记函数
#include
#include
using namespace std;
class student{
private :
int age ;
string name;
public :
void read(string bookname){
cout<< bookname << endl;
}
void speak(string something);
};
void student::speak(string something){
cout << "说说说---" << something << endl;
}
int main(){
student stu;
stu.read("哈利波特");
stu.speak("我喜欢看哈利波特");
return 0 ;
}
当定义一个类后,它的对象在未来的操作中,总会不可避免的总会碰到如下的行为:创建 、拷贝 、赋值 、移动 、销毁 。这些操作实际上是通过六种特殊的成员函数来控制的:构造函数 、析构函数、拷贝构造函数 、拷贝赋值函数 、移动构造函数、移动赋值函数 。默认情况下,编译器会为新创建的类添加这些函数 (默认不会添加移动构造和移动赋值),以便它的对象在未来能够执行这些操作。
- 构造函数是类的一种特殊的成员函数,它会在每次创建对象时执行。它与类名同名,没有返回值,可以被重载,通常用来做初始化工作。
- 析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。不能被重载,一般用于释放资源。
#include
#include
using namespace std;
class student{
string name;
int age ;
public :
//构造函数
student(){
cout << "执行无参构造函数" <<endl;
}
student(string name ){
cout << "执行含有一个参数的参构造函数" <<endl;
}
student(string name , int age ){
cout << "执行含有两个参数的构造函数" <<endl;
}、
//更好的构造方式
student(string name ,int age):name{name},age{age}{
cout << "执行有参构造函数" <<endl;
}
//析构函数{}
~student(){
cout << "执行析构函数" <<endl;
}
};
int main(){
student *s1 = new student();
student s2{"张三"};
student s3{"张三",28};
//释放对象
delete s1;
return 0 ;
}
C++中经常使用一个常量或变量初始化另一个变量,表面上相当于将s1中每个数据成员的值复制到s2中。实际上,系统调用 了一个拷贝构造函数。
class student{ };
int mian(){
int a = 3;
int b = a;
student s1;
student s2 = s1;
return 0 ;
}
指的是在对象复制时,只对对象中的数据成员进行简单的赋值,编译器提供的拷贝操作即是浅拷贝,默认拷贝构造函数执行的也是浅拷贝。如果数据中有属于动态成员 ( 在堆内存存放 ),那么浅拷贝只是做指向而已,不会开辟新的空间。
默认情况下,浅拷贝已经足以应付日常的需求了,但是当类中的成员存在动态成员(指针)时,浅拷贝往往会出现一些奇怪的问题。由于address是指针类型,如果是浅拷贝, 那么两个指针会指向同一个位置。
深拷贝 也是执行拷贝,只是在面对对象含有动态成员或指针时,会执行新内存的开辟,而不是作简单的指向。在发生对象拷贝的情况下,如果对象中存在动态成员,就不能仅仅简单地赋值了,而应该重新分配空间。
#include
#include
using namespace std;
class student {
public:
string name;
string *address=nullptr;
student(string name , string * address):name(name),address(address){
cout << "执行构造函数" << endl;
}
//深拷贝
student(const student & s){
cout << "调用了拷贝构造函数" << endl;
name = s.name;
if(address == nullptr){
//开辟新的空间
address = new string();
*address = *s.address;
}
}
//析构函数
~student(){
cout << "调用了析构函数" << endl;
if(address != nullptr){
delete address; address = nullptr;
}
}
};
int main(){
string *address= new string("深圳");
student s1("张三" , address);
//此处会执行拷贝。
student s2 = s1;
cout << s1.name << " : " << *(s1.address) << endl;
cout << s2.name << " : " << *(s2.address) << endl;
//修改第一个学生的地址为:北京
*(s1.address) = "北京";
//第二个学生的地址不会变成北京
cout << s1.name << " : " << *(s1.address) << endl;
cout << s2.name << " : " << *(s2.address) << endl;
return 0 ;
}
重载就是对已有的东西赋予新的含义
运算符重载一般格式:
函数类型 operator 运算符名称 (型参表)
{对运算符的重载处理}
int operator + (int a,int b)
{return (a+b);}
继承和派生的概念
一个新类从已有的类建立一个新类就是继承,而新产生的子类就是类的派生
派生类的声明方式:
> class 派生类名:[继承方式] 基类名
{
派生类新增加的成员
};
注:如果不写继承方式,默认为private(私有的)
Class Student { //声明基类
public: //基类公用成员
void get_value() //输入基类数据的成员函数
{cin>>num>>name>>sex;}
void display() //输出基类数据的成员函数
{
cout<<" num: "<<num<<endl;
cout<<" name: "<<name<<endl;
cout<<" sex: "<<sex<<endl;
}
private : //基类私有成员
int num;
string name;
char sex;
};
class Student1: public Student { //以public方式声明派生类Student1
public:
void get_value_1() //输入派生类数据
{cin>>age>>addr;}
void display_1()
{
cout<<" num: "<<num<<endl; //试图引用基类的私有成员,错误
cout<<" name: "<<name<<endl; //试图引用基类的私有成员,错误
cout<<" sex: "<<sex<<endl; //试图引用基类的私有成员,错误
cout<<" age: "<<age<<endl; //引用派生类的私有成员,正确
cout<<" address: "<<addr<<endl;
} //引用派生类的私有成员,正确
Private:
int age;
string addr;
};
多态性:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)。
多态性分类:
静态多态性是通过函数重载实现的
动态多态性的特点是: 不在编译时确定调用的是哪个函数,而是在程序运行过程中才动态地确定操作所针对的对象。动态多态性是通过虚函数(virtual function)实现的。
虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数
#include
#include
using namespace std;
//声明基类Student
class Student {
public:
Student(int, string,float);//声明构造函数
void display(); //声明输出函数
protected://受保护成员,派生类可以访问
int num;
string name;
float score;
};
//Student类成员函数的实现
Student∷Student(int n, string nam,float s) //定义构造函数
{num=n;name=nam;score=s;}
void Student∷display()//定义输出函数
{cout<<"num:"<<num<<"\\nname:"<<name<<"\\nscore:"<<score<<"\\n\\n";}
//声明公用派生类Graduate
class Graduate:public Student {
public:
Graduate(int, string, float, float);//声明构造函数
void display(); //与基类的输出函数同名
private:
float wage;
};
// Graduate类成员函数的实现
Graduate∷Graduate(int n, string nam,float s,float w):Student(n,nam,s),wage(w) {}
void Graduate∷display()//定义输出函数
{cout<<"num:"<<num<<"\\nname:"<<name<<"\\nscore:"<<score<<"\\nwage="<<wage<<endl;}
//主函数
int main()
{
Student stud1(1001,"Li",87.5); //定义Student类对象stud1
Graduate grad1(2001,"Wang",98.5,1200);//定义Graduate类对象grad1
Student pt=&stud1;//定义指向基类对象的指针变量pt,指向stud1
pt->display(); //输出Student(基类)对象stud1中的数据
pt=&grad1;//pt指向Graduate类对象grad1
pt->display();//希望输出Graduate类对象grad1中的数据
return 0;
}
纯虚函数:在声明虚函数时被“初始化”为0的函数。作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。其格式为:
virtual 函数类型 函数名 (参数表列) =0;
抽象类:这种不用来定义对象而只作为一种基本类型用作继承的类,称为抽象类(abstract class),由于它常用作基类,通常称为抽象基类(abstract base class)。
C++的输入输出流是指由若干个字节组成的字节序列,这些字节中的数据按顺序从一个对象传送到另一个对象。总之,流是与内存缓冲区相对应的,或者说,缓冲区中的数据就是流。
ios是“输入输出流”,streambuf是处理“流缓冲区”的类,包括缓冲区起始地址、读写指针和对缓冲区的读写操作,是数据在缓冲区中的管理和数据输入输出缓冲区的实现。I/O类库中的常用流类:
一般形式为:
文件流对象.open(磁盘文件名,输入输出方式);
文件输入输出方式设置值:
方式 | 作用 |
---|---|
ios::in | 以输入方式打开文件 |
ios:out | 以输出方式打开文件,如果文件存在,则将其原有内容清除 |
ios::app | 以输出方式打开文件,写入的数据添加在文件末尾 |
ios::ate | 打开已有文件,文件指针指向文件末尾 |
ios::trunc | 打开文件,如果文件存在则删除其数据;不存在则创建文件 |
ios:binary | 以二进制方式打开一个文件 |
ios::nocreate | 打开一个已有文件,如果文件不存在,则打开失败 |
ios::noreplace | 如果文件不存在则建立文件,如果存在则操作失败 |
ios::in | ios::out | 以输入输出方式打开文件 |
ios::binary | ios::out | 以二进制方式打开一个输出文件 |
ios::in | ios::binary | 以二进制方式打开一个输入文件 |
ofstream outfile;//定义ofstream类(输出文件流类)对象outfile
outfile.open("f1.dat",ios∷out);//使文件流与f1.dat文件建立关联
outfile.close();//将输出文件流所关联的磁盘文件关闭所谓关闭
outfile.open("f2.dat",ios∷app|ios∷nocreate);此时文件流outfile与f2.dat建立关联,并指定了f2.dat的工作方式。
字符指针buffer指向内存中一段存储空间。len是读写的字节数。其一般格式为:
istream& read(char *buffer,int len);
ostream& write(const char *buffer,int len);
异常处理指的是,对运行时出现的差错以及其他例外情况的处理 。C++异常处理由3个部分组成:检查(try)、抛出(throw)和捕捉(catch)。如果出现异常,可以发出一个信息,由上级捕捉信息后处理
void f3()
{double a=0;
try //在try中包括要检测的部分
{throw a;} //throw执行后会立刻返回上一级函数
catch(float) //用catch捕捉异常并处理
{cout << "ERROR!" << endl;}
}
可以使用 using namespace指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。
本质上,命名空间就是定义了一个范围。它可作为附加信息来区分不同库中相同名称的函数、类、变量等。
#include
using namespace std;
int main(){
cout<<"hi c++" << endl;
return 0 ;
}
关于python语法的学习见以下文章
https://blog.csdn.net/weixin_44567668/article/details/125189798