1.注释
单行注释://
多行注释:/* 内容 */
// 注释一行
/*
注
释
很
多
行
*/
2.数据类型
1.整型 int
2.短整型 short
3.长整型 long
4.长长整型 long long
5.单精度浮点型 float(保留6位有效数字)
6.双精度浮点型 doublt(保留15位有效数字)
7.字符型 char
8.字符串型 string(可能需要导入头文件 <string>)
9.布尔型 bool
3.查看变量占用内存空间大小
sizeof(变量)
4.算数运算符
±*/% 加减乘除取模
++ – 递增递减
a++ 等价于 a = a + 1
b = a++ 等价于 b = a; a = a + 1
b = ++a 等价于 a = a + 1; b = a
5.逻辑运算符
! 非
&& 与
|| 或
6.选择结构 switch
如果case匹配不成功时,执行default的内容。
int score = 1;
switch(score){
case 1:....
case 2:...
....
default:...
}
注意:如果case内部没有break,当匹配成功后,会继续往下执行所有case的代码。要匹配成功后跳出,必须加break。
7.跳转语句
break 跳出
continue 跳过下面部分
for(int i = 1; i < 10; i ++){
代码部分1
if(....){
跳转语句
}
代码部分2
}
如果跳转语句是break,当if成立后,直接跳出循环;如果是continue,当if成立后,代码部分2不执行,i+1继续执行循环。
8.数组
1.一维数组初始化
int arr[5];
int arr[] = {1,2,3,4,5};
2.二维数组初始化
int arr[2]\[3];
int arr[2]\[3] = {{1,2,3},{2,3,4}};
int arr[2]\[3] = {1,2,3,2,3,4};
int arr[]\[3] = {1,2,3,2,3,4};
注意:二维数组初始化时,如果有直接给数组赋值,那么数组行数可以省略,但列数不行。
9.函数
定义
返回值类型 函数名(变量){
函数体
}
10.指针
/*
初始化: int *p;
赋值: p = &变量;
取值(指针指向的变量的值):*p;
*/
cout << "指针" << endl;
int a = 123;
int * p;
p = &a;
cout << "a的地址:" << &a << ",p的值" << p << "," << "*p=" << *p <<endl;
// &a == p;*p == a
*p = 122;
cout << "a = " << a << ",*p = " << *p << endl;
//因为p指向的是a的内存地址,所以修改*p等价于修改a的值,因此此时a == 122
11.const修饰指针
int a = 123;
const int * p = &a;
//常量指针可以修改指向,但无法修改所指向的变量的值。
int a = 123;
int * const p = &a;
//指针常量可以修改所指向变量的值,但无法修改指向。
int a = 123;
const int * const p = &a;
12.指针与函数
void swap1(int a,int b){
int tmp;
tmp = a;
a = b;
b = tmp;
}
void swap2(int *p1,int *p2){
int tmp;
tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
int main(){
int a = 1;
int b = 2;
swap1(a,b);
swap2(&a,&b);
//调用swap1 a,b的值不变,调用swap2,此时传入的是变量的内存地址,a,b的值会改变。
}
//
13.结构体
//结构体创建,struct不可省略
struct Student{
int age;
int score;
string name;
};//注意有个分号
//结构体初始化,struct可省略
//法1
struct Student s1;
s1.age = 12;
s1.ascore = 100;
s1.name = "xiaoming";
//法2
struct Student s2 = {12,100,"xiaoming"};
//结构体数组
struct Student stuArr[3];
strArr[0] = {13,99,"xiaohong"};
strArr[1] = {14,98,"xiaolan"};
strArr[2] = {14,98,"xiaoqing"};
struct Student *p;
struct Student s = {12,100,"xiaoming"};
p = &s;
//结构体指针取值用 ->
cout << "年龄:" << p -> age << endl;
p -> age = 15;
cout << "年龄:" << p -> age << endl;
void printStu(Student std){
cout << "年龄:" << std.age << endl;
std.age = 1222;//不修改s的age值。
}
//使用指针,与函数的指针一样
void printStu(Student *std){
cout << "年龄:" << std -> age << endl;
std -> age = 1222;//修改了s的ageg值。
}
int main(){
struct Student s = {14,98,"xiaoqing"};
printStu(s);
printStu2(&s);
}
void printStu(const struct Student *stu){
// 加入const修饰,则stu的属性只能读取,无法修改
stu -> age = 123;//错误
}
14.引用
int a = 123;
int &b = a;
//引用在创建时必须直接初始化(指定引用对象),且初始化后无法修改引用对象。
cout << b >> endl;
//输出123
int c = 12;
b = c //该操作是修改a或者说b的值,而不是将b引用向c
int a = 123;
int &b = a;
a = 124;
cout << b << endl;
//输出 124
b = 125;
cout << a << endl;
//输出125
void swap(int &a,int &b){
//调用该函数,实参也会被修改,因为引用的本质就是指针
int temp;
a = temp;
a = b;
b = temp;
}
int main(){
int a = 12;
int b = 21;
swap(a,b)
}
int & f(){
//错误示例:返回局部变量的引用
int a = 10;//局部变量保存在栈区,f执行完可能就被释放了(视编译器不同,可能不会立即释放,会保留一次)
return a;
}
int g_a = 123;//全局变量存放在全局区,程序运行完才会被清除
int & f2(){
//正确示例:返回全局变量的引用
return g_a;
}
int & f3(){
static int a = 10;//静态变量存放在全局区,程序结束后由系统释放。
return a;
}
int main(){
int &res = f();
cout << res << endl;
//第一次输出的res是正确的,编译器会对局部变量做一次保留。(并不一定能正确输出,与编译器有关)
cout << res << endl;
//第二次输出的re是错误的,因为局部变量此时已经被清除了。
int &res2 = f2()
cout << res2 << endl;
cout << res2 << endl;
cout << res2 << endl;
//全局变量在程序运行中不会被清除,因此返回全局变量的引用是合法的。
int &res3 = f3()
cout << res3 << endl;
cout << res3 << endl;
cout << res3 << endl;
//静态变量在程序运行中不会被清除,因此返回静态变量的引用是合法的。
return 0;
int &f(){
static a = 10;
return a;
int main(){
int &a = f();
f() = 123;
cout << a << endl;
//输出123
}
int &f(){
static int a = 123;
return a;
}
int main(){
int &a = f();
int &b = f();
f() = 12345;
cout << a << endl;
cout << b << endl;
//a=12345,b=12345
a = 222;
cout << a << endl;
cout << b << endl;
// a = 222,b = 222
//该结果再次说明,引用的本质是指针,所有的修改都是基于地址的。
}
int main(){
int a = 10;
int &aa = a; //合法
int &bb = 10;//非法
const int &cc = 10;//合法,编译器会自动生成一个临时变量存放10,然后将cc指向临时变量
}
常量引用主要用于防止误操作
void f(const int &a){
//防止误操作,修改了a
cout << a << endl;
}
15.函数默认参数
//声明
int f(int a,int b)
//定义
int f(int a,int b = 10){
return a + b;
}
//或者
//声明
int f(int a,int b = 10)
//定义
int f(int a,int b){
return a + b;
}
int f(int a,int b,int c = 123,int d = 234){
return a + b + c + d;
} //正确
int f(int a,int b = 10,int c,int d = 234){
return a + b + c + d;
} //错误
16.函数的占位参数
void f(int a,int)
int main(){
f(10,10);
}
17.函数的重载
void f(int a){
cout << "int" << endl;
}
void f(float a){
cout << "float" << endl;
}
void f(double a){
cout << "double" << endl;
}
void f(float a,int b){
cout << "float int" << endl;
}
void f(int a,float b){
cout << "int float" << endl;
}
int main(){
f(1);
f(1.2f);
f(1.2);
f(1.2,1);
f(1,1.2);
}
void f(int &a){
cout << "&" << endl;
}
void f(const int &a){
cout << "const &" << endl;
}
int main(){
int a = 10;
f(a);
const int aa = 10;
f(aa);
void f(int a,int b = 10){
...
}
void f(int a){
...
}
int main(){
f(10);//非法操作,此时的参数形式满足上述两个函数
f(10,10);//合法操作,此时能区分调用哪个f
}
18.类和对象
class 类名{
访问权限
属性
方法
};//此处需要一个分号
class Car{
public:
string car_name = "Tesla";
void show_car(){
cout << "car_name:" << car_name << endl;
}
};
struct p1{
int age = 10;
void show(){
cout << "age:" << age << endl;
}
};
class p2{
int age = 10;
void show(){
cout << "age:" << age << endl;
}
};
int main(){
p1 p11;
p2 p22;
p11.ag;//合法操作
p22.age;//非法操作
}
class Person{
private:
//属性设为私有
int age;
string name;
public:
//通过编写getter和setter函数来对外提供访问和修改接口
void setName(string new_name){
//setter
name = new_name;
}
string getName(){
//getter
return name;
}
void setAge(int new_age){
//控制修改数据的合法性
if(new_age <0 or new_age > 100){
cout << "非法数据" << endl;
}else{
age = new_age;
}
}
class Person{
public:
int a = 10;
static int b;
};
int Person::b = 100;
int main(){
Person p1;
Person p2;
cout << p1.b << "," << p2.b << endl;
p1.a = 123;
cout << p1.a << "," << p2.a << endl;
//p1.a = 123,p2.a = 10
p1.b = 1220;
cout << p1.b << "," << p2.b << endl;
//p1.b = 1220,p2.b = 1220,p1和p2共享静态变量b
}
```
19.对象的初始化和清理
class Person{
private:
//属性设为私有
int age;
string name = "";
public:
void setName(string new_name){
name = new_name;
}
string getName(){
return name;
}
//构造函数,执行在所有代码前
Person(){
cout << "构造函数测试" << endl;
}
//析构函数,最后(对象销毁前)执行
~Person(){
cout << "析构函数测试" << endl;
}
};
int main(){
Person p;
p.setName("coco");
cout << p.getName() << endl;
//输出顺序 "构造函数测试" "coco" "析构函数测试"
}
class Person{
public:
Person(){
//无参构造为默认的构造函数
cout << "无参构造函数测试" << endl;
}
Person(int a){
//有参构造函数
cout << "有参构造函数测试" << endl;
}
Person(const Person &p){
cout << "拷贝构造函数测试" << endl;
//括号法
Person p1;//无参,无参构造不能加(),否则会当成函数声明
Person p2(1);//有参
Person p3(p2);//拷贝
//显示法
Person p1;
Person p2 = Person(10);
Person p3 = Person(p2);
Person(10); //匿名对象,当前 行执行完后,对象立即被回收。拷贝构造函数无法创建匿名对象。
//隐式转换法
Person p4 = 10;//等价于 Person p4 = Person(10);
Person p5 = p4;等价于 Person p5 = Person(p4);
class Person{
public:
int age = 10;
string name = "A";
//当我们未定义构造和析构函数时,编译器会自动给类添加以下三个函数
Person{
//无参构造函数,函数体为空
}
Person(const Person &p){
//拷贝构造函数,
age = p.age;
name = p.name;
}
~Person{
//无参析构函数,函数体为空
}
};
class Person{
public:
int age = 10;
string name = "A";
Person(int a){
//有参构造函数,函数体为空
}
//编译器不提供无参构造函数,但仍提供拷贝构造函
Person(const Person &p){
//拷贝构造函数,
age = p.age;
name = p.name;
}
~Person{
//无参析构函数,函数体为空
}
};
20.浅拷贝与深拷贝
int a = 12;
int b = a;
//深拷贝,a和b的内存地址不一样
int p = 12;
int *p1;
p1 = &p;
int *p2 = p1;
//浅拷贝,p1和p2指向相同内存地址
//要对指针进行深拷贝,需要自己编写拷贝代码
int pp = 123;
int *pp1;
pp1 = &pp;
int *pp2 = new int(*pp1);
21.类中静态成员
class Person{
public:
static void f(){
cout << "static void f" << endl;
a = 123;//正确,静态成员函数可以访问静态成员变量
b = 123;//错误,静态成员函数无法访问非静态成员变量
}
static int a;//静态成员变量,类内声明
int b;//非静态成员变量
}
int Person::a = 12;//类外初始化
int main(){
//访问静态成员函数方法1,通过对象访问
Person p1;
p1.f();
//访问静态成员函数方法二,通过类名访问
Person::f();
Person p2;
p1.a = 123;
//此时p1.a == 123,p2.a == 123,说明所有对象共享一份静态变量数据
}
class Person{
int a;//非静态成员变量,属于类的对象上
static int b;//静态成员变量,不属于类对象
void func() {} //非静态成员函数,不属于类对象
static void func1(){} //静态成员函数,不属于类对象
}
int main(){
Peroson p1;
cout << sizeof(p1) << endl;
// sizeof(p1) == 4,因为只有a变量属于类
}
22.this指针
class Person{
int a;
void Person(int a){
a = a;//错误操作
this -> a = a;//正确操作,this指向对象本身
}
}
22.const修饰成员函数
class Person{
public:
int a = 10;
void f() const {
//const修饰this,称为常函数,此时无法修改
this -> a = 123;//错误操作
}
class Person{
public:
mutable int a = 10;
void f() const {
//const修饰this,再加mutable,此时可以修改this属性
this -> a = 123;//正确操作
}
};
int main(){
const Person p1;//常对象,只能调用常函数
}
23.友元
//全局函数做友元,仅需在类首添加friend+全局函数声明
class Person{
friend main();//全局函数main做友元
private:
int b = 123;
};
int main(){
Person p1;
p1.b = 1234;
cout << p1.b <<endl;
}
//类做友元,仅需在类首添加friend+类声明
class Person1{
friend class Person2;//类Person2做友元
private:
int b = 123;
};
class Person2{
friend void f();//全局函数f做友元
private:
Person1 *p;
Person2(){
p = new Person1;
cout << p -> b << endl;
}
private:
int x = 123;
};
void f(){
Person2 p2;
cout << p2.x << endl;
}
int main(){
f();
}
//成员函数做友元示例
//成员函数需类内声明,类外实现,否则编译通不过
//因为C++编译过程是一行一行扫的,如果在类内直接实现函数,则实现函数时可能会用到之后才定义的函数或类,造成编译失败
//错误示范
class p2;
class p1{
public:
p2 *p;
p1(){
p = new p2();//编译器找不到p2的具体实现
}
};
class p2{
public:
int a = 123;
};
int main()
{
p1 p;
}
//正确
class p2;
class p1{
public:
p2 *p;
p1();
};
class p2{
public:
int a = 123;
};
p1::p1(){
p = new p2();//此时编译器已经知道p2的具体实现
}
int main()
{
p1 p;
}
//*********
class Building;
class goodGuy{
public:
goodGuy();
void visit();
private:
Building *building;
};
class Building{
friend void goodGuy::visit();//成员函数做友元
public:
Building();
string sittingname;
private:
string bedroom;
};
Building::Building()
{
this -> sittingname = "A";
this -> bedroom = "B";
}
goodGuy::goodGuy(){
building = new Building;
}
void goodGuy::visit(){
cout << building -> sittingname << endl;
cout << building -> bedroom << endl;
}
void test01(){
goodGuy gg;
gg.visit();
}
int main(){
test01();
}
24.运算符重载
//以成员函数方式重载+
class Person{
public:
Person(){
}
Person(int a,int b){
this -> a = a;
this -> b = a;
}
int a;
int b;
Person operator+(Person p){
Person pp;
pp.a = this -> a + p.a;
pp.b = this -> b + p.b;
return pp;
}
};
int main()
{
Person p1(1,2);
Person p2(2,3);
Person p3 = p1 + p2;
cout << p3.a << ";" << p3.b << endl;
}
class Person{
public:
Person(){
}
int a;
int b;
};
//通过全局函数重载+
Person operator+(Person p1,Person p2){
Person p;
p.a = p1.a + p2.a;
p.b = p1.b + p2.b;
return p;
}
int main()
{
Person p1(1,2);
Person p2(2,3);
Person p3 = p1 + p2;
cout << p3.a << ";" << p3.b << endl;
}
25.继承
class Person1 : public Person2{
};
//Person1继承Person2
父类权限 | 子类继承得到的权限 |
---|---|
public | public |
protected | protected |
private | 不可得 |
父类权限 | 子类继承得到的权限 |
---|---|
public | protected |
protected | protected |
private | 不可得 |
父类权限 | 子类继承得到的权限 |
---|---|
public | private |
protected | private |
private | 不可得 |
class father{
public:
int a = 123;
void f(){
cout << "父类" << endl;
}
};
class son:public father{
public:
int a = 12;
void f(){
cout << "子类" << endl;
}
};
int main(){
son s;
cout << s.a << endl;//12
cout << s.father::a << endl;//123
s.f();//子类
s.father::f()//父类
son::father::f()//直接用类名访问
}
#include <iostream>
using namespace std;
#include <string>
class Person{
public:
Person(){cout << "aaaaa" << endl;};
Person(int age,string name){
this -> age = age;
this -> name = name;
cout << "aacccca" << endl;
}
int age = 1;
string name = "a";
};
class Person1:public Person{
public:
Person1(){};
Person1(int a,string b):Person(a,b){
}
void f(){
cout << this -> age << "," << this -> name << endl;
}
};
int main(){
Person1 p1(12,"a");
p1.f();
return 0;
}
class Person1{
public:
int a = 1;
};
class Person2{
public:
int a = 2;
};
class Person:public Person1,public Person2{
public:
int a = 3;
};
int main()
{
Person p1;
cout << p1.a << endl;//输出3
//重名,加作用域
cout << p1.Person2::a << endl;//输出2
cout << p1.Person1::a << endl;//输出1
}
如上图所示,羊类和驼类均继承了动物类,而草泥马又同时继承了羊类和驼类,该种继承方式称为棱形继承。
棱形继承会带来数据二义性的问题,即草泥马同时继承了动物类中的两份数据,导致数据重复并且这两份数据不一定一致
class Animal{
public:
int age;
};
class Sheep:public Animal{
public:
Sheep(){
this -> age = 1;
}
};
class Camel:public Animal{
public:
Camel(){
this -> age = 2;
}
};
class Alpaca:public Sheep,public Camel{
};
int main()
{
Alpaca A;
//重名,加作用域
cout << A.Sheep::age << endl;//输出1
cout << A.Camel::age << endl;//输出2
//添加作用域可以解决重名问题,但是仍会造成资源浪费(存在两份数据),且会产生歧义
}
解决办法:虚继承 virtual
class Animal{
public:
int age;
};
class Sheep:virtual public Animal{
public:
Sheep(){
this -> age = 1;
}
};
class Camel:virtual public Animal{
public:
Camel(){
this -> age = 2;
}
};
class Alpaca:public Sheep,public Camel{
};
int main()
{
Alpaca A;
//虚继承,共享Animal类的一份数据
cout << A.Sheep::age << endl;//输出2
cout << A.Camel::age << endl;//输出2
cout << A.age << endl; //可以直接访问
A.Sheep::age = 123;
cout << A.Sheep::age << endl;//输出123
cout << A.Camel::age << endl;//输出123
cout << A.age << endl; //输出123
A.age = 12;
cout << A.Sheep::age << endl;//输出12
cout << A.Camel::age << endl;//输出12
cout << A.age << endl; //输出12
return 0;
}
26.多态
//示例
class Animal{
public:
void speak(){
cout << "动物在说话" << endl;
}
};
class Dog:public Animal{
public:
void speak(){
cout << "狗在汪汪叫" << endl;
}
};
//地址早绑定,在编译阶段就确定了地址,因此即便传入的参数为Animal的子类,执行的speak函数也是Animal的
void f(Animal &animal){
animal.speak();
}
int main(){
Dog dog;
f(dog);
//编译前即绑定speak函数地址,因此输出动物在说话
}
//添加virtual关键字,将Animal类中speak转为虚函数即可实现动态多态
class Animal{
public:
virtual void speak(){
cout << "动物在说话" << endl;
}
};
int main(){
Dog dog;
f(dog);
//运行时才绑定speak函数地址,因此输出狗在汪汪叫
}
27.纯虚函数和抽象类
class Animal{
public:
virtual void speak() = 0;//纯虚函数
//语法:virtual 返回值类型 函数名(参数列表) = 0;
};
//具有纯虚函数speak,因此是抽象类
class Animal{
public:
virtual void speak() = 0;
};
int main(){
Animal animal;//错误操作,抽象类无法实例化
}
//具有纯虚函数speak,因此是抽象类
class Animal{
public:
virtual void speak() = 0;
};
class Dog:public Animal{
//重写纯虚函数
void speak(){
cout << "dog dog" <<endl;
}
};
int main(){
Dog dog;
}
28.虚析构和纯虚析构
class Animal{
public:
Animal(){
cout << "Animal诞生" << endl;
}
virtual void f() = 0;
~Animal(){
cout << "Animal退出" << endl;
}
};
class Dog:public Animal{
public:
void f(){
cout << "Dog " << endl;
}
Dog(){
cout << "Dog诞生" << endl;
x = new int(12);
}
int *x;
~Dog(){
if( x != NULL){
//未被访问
cout << "Dog退出" << endl;
delete x;
x = NULL;
}
}
};
int main(){
Animal * animal = new Dog;
animal -> f();
delete animal;
/*输出:
Animal诞生
Dog诞生
Dog
Animal退出
/*
//父类指针指向子类,父类指针在析构时不会调用子类的析构函数,因此如果子类在堆区中有数据,会出现内存泄漏问题
}
//虚函数版本
class Animal{
public:
Animal(){
cout << "Animal诞生" << endl;
}
virtual void f() = 0;
//仅在此处添加virtual即可
virtual ~Animal(){
cout << "Animal退出" << endl;
}
};
class Dog:public Animal{
public:
void f(){
cout << "Dog " << endl;
}
Dog(){
cout << "Dog诞生" << endl;
x = new int(12);
}
int *x;
~Dog(){
if( x != NULL){
cout << "Dog退出" << endl;
delete x;
x = NULL;
}
}
};
int main(){
Animal * animal = new Dog;
animal -> f();
delete animal;
/*输出:
Animal诞生
Dog诞生
Dog
Dog退出
Animal退出
*/
}
//纯虚析构版本
class Animal{
public:
Animal(){
cout << "Animal诞生" << endl;
}
virtual void f() = 0;
//纯虚析构函数 类内声明
virtual ~Animal() = 0;
};
class Dog:public Animal{
public:
void f(){
cout << "Dog " << endl;
}
Dog(){
cout << "Dog诞生" << endl;
x = new int(12);
}
int *x;
~Dog(){
if( x != NULL){
cout << "Dog退出" << endl;
delete x;
x = NULL;
}
}
};
//纯虚析构函数 类外访问
Animal::~Animal(){
cout << "Animal退出" << endl;
}
int main(){
Animal * animal = new Dog;
animal -> f();
delete animal;
/*输出:
Animal诞生
Dog诞生
Dog
Dog退出
Animal退出
*/
}