您的位置:首页 > 科技 > 能源 > 新注册的公司在哪里可以查到_济南网络营销外包服务_友情链接网_百度热门关键词

新注册的公司在哪里可以查到_济南网络营销外包服务_友情链接网_百度热门关键词

2024/11/15 7:16:52 来源:https://blog.csdn.net/m0_64837052/article/details/142897916  浏览:    关键词:新注册的公司在哪里可以查到_济南网络营销外包服务_友情链接网_百度热门关键词
新注册的公司在哪里可以查到_济南网络营销外包服务_友情链接网_百度热门关键词

第一部分 基础语法入门

一、基础

1、变量与常量

1、变量

        变量存在的意义:方便管理内存空间

2、常量

        用于记录程序中不可更改的数据

        #define 常量名 常量值

        const 数据类型 常量名=常量值 ;      

2、数据类型

1、整型

 short        2字节

 int        4字节    

 long        Win4字节,32位linux 4字节,64位linux 8字节

 long long        8字节

2、实型(浮点型)

 float         4字节        7位有效数字

 double         8字节        15-16位有效数组

 默认情况下,输出一位小数,会显示6位有效数字

 科学计数法

         float f1=3e2;        //3*10^2

         float f2=3e-2;        //3*0.1^2

3、字符型

 char         1字节

 字符型并不是把字符本身放在内存中存储,而是将对应的ASCII编码存储

4、字符串

 char 变量名[]="abc"

 string 变量名="abc" //include <string>

5、bool

 1字节

6、数组

 int a[5];

 int b[5]={1,2,3,4,5};

 int c[]={1,2,3};

3、sizeof关键字

统计数据类型所占内存大小

4、案例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void qOne()
{int a = 4;a += (a++);//9    5+=4 执行a++ 返回4,a递增为5a += (++a);//10 5+=5//(a++)+= a;/*a++是一个右值,即不可修改的临时值,+=需要一个左值(即可以被赋值或修改的对象)*/(++a) += (a++);//10/*++a a变为5,返回5,++a作为左值a++ 当前a为5,返回5,a自增为65+=5*/printf("%d\n", a);
}
void Foo(char str[100])
{                                // 将str被视为char*传递,所以打印的是指针大小printf("%d\n", sizeof(str)); // 8 32位操作系统中,指针大小4字节,64位8字节
}
void qTwo()
{char str[] = "Hello word";char *p = str;int n = 10;printf("%d\n", sizeof(str)); // 11printf("%d\n", sizeof(p));   // 64位8printf("%d\n", sizeof(n));   // 4// Foo(str);void *p1 = malloc(100);// sizeof(p1) 返回的是指针本身的大小,而不是 malloc 分配的内存的大小。指针的大小依赖于编译器和操作系统的位数。printf("%d\n", sizeof(p1)); // 8
}
struct Test
{int Num;bool b;char sz[10];
};void qThree()
{// 使用了c++的特性new,不能使用gcc编译// Test* p=new Test[10];struct Test *p = (struct Test *)malloc(10 * sizeof(struct Test));Test *p1 = &p[0];Test *p2 = &p[8];int n = p2 - p1;printf("%d", n); // 8
}void qThreePlus()
{// 使用 malloc 分配内存,分配空间足够存放10个 Test 结构体void *p = new Test[10];/*void *p1 = (char *)p;                           // 指向第一个 Test 结构体void *p2 = (char *)p + 8 * sizeof(struct Test); // 指向第九个 Test 结构体printf("sizeof(struct Test):%d\n", sizeof(struct Test));// 因为 p1 和 p2 是 void*,我们需要将它们转换为适当的类型才能进行运算int n = (char *)p2 - (char *)p1;//128
*//* Test *p1 = (Test *)((char *)p + 0 * sizeof(struct Test)); // 指向第一个 Test 结构体Test *p2 = (Test *)((char *)p + 8 * sizeof(struct Test)); // 指向第九个 Test 结构体int n = p2 - p1;*/Test *p1 = (Test *)p; // 指向第一个 Test 结构体Test *p2 = (Test *)p + 8; // 指向第九个 Test 结构体int n = p2 - p1;printf("%d\n", n); // 输出 8
}char *qFour()
{char *a = new char[128];char *p = a;strcpy(p, "aaaa");return p;
}
char *qFive()
{// 拷贝不了:局部变量,栈上分配内存,函数结束,内存空间会被回收// char a[128];// 使用malloc或静态数组或者new,生命周期将被延长static char a[128];// 为什么不能是&a,他是指向包含128字符的数组的指针,不是字符,类型是char (*)[128],不是char*char *p = a;strcpy(p, "aaaa");return p;
}
char *strcpy(char *strDest, const char *strSrc)
{char *oStrDest = strDest;while (*strSrc != '\0'){*strDest = *strSrc;strSrc++;strDest++;}*strDest = '\0';return oStrDest;
}
char *strcpyPlus(char *strDest, const char *strSrc)
{int srcLen = strlen(strSrc);char *oStrDest = strDest;if (strDest < strSrc || strDest - strSrc >= srcLen){// 没有内存重叠问题while (*strSrc != '\0'){*strDest = *strSrc;strSrc++;strDest++;}*strDest = '\0';return oStrDest;}// 有内存重叠问题strDest = strDest + srcLen;strSrc = strSrc + srcLen - 1;*strDest = '\0';for (int i = 0; i < srcLen; i++){strDest--;*strDest = *strSrc;strSrc--;}return oStrDest;
}void *memcpy(void *dest, const void *src, int size)
{char *cDest = (char *)dest;const char *cSrc = (const char *)src;for (int i = 0; i < size; i++){cDest[i] = cSrc[i];}return dest;
}
void *memcpyOtherWay(void *dest, const void *src, int size)
{char *cDest = (char *)dest;const char *cSrc = (const char *)src;for (int i = 0; i < size; i++){*cDest = *cSrc;cDest++;cSrc++;}return dest;
}

二、函数

1、函数的分文件编写

作用:让代码结构更加清晰

1、创建.h头文件

2、创建.cpp源文件

3、在头文件写函数的声明

4、在源文件写函数的定义

//值交换
//1、swap.h
#include <iostream>
using namespace std;void swap(int a,int b);//2、swap.cpp
#include "swap.h"void swap(int a,int b){int temp=a;a=b;b=temp;cout << "a=" << a <<endl;cout << "b=" << b <<endl;
}//3、main.cpp
#include <iostream>
#inculde "swap.h"
using namespace std;int main(){swap(1,2);.return 0;
}

2、函数默认参数

函数的形参列表是可以有默认值的

#include <iostream>using namespace std;
int add(int a,int b,int c);
int add(int a,int b=20,int c=30){return a+b+c;
}
int main(){add(10);//60add(10,30);//70}
/*
1、如果某个位置已经有了默认参数,那么从这个位置往后,从左往右都必须有默认值int add(int a,int b=20,int c=30)
2、如果函数声明有默认参数,函数的实现就不能有默认参数(声明和实现只能有一个有默认参数)*/

3、函数占位参数
#include<iostream>
using namespace std;
void func1(int a,int){}
//占位参数可以有默认参数
void func2(int a,int =20){}
int main(){func1(10,20);}

4、函数重载 

条件:

        1、同一个作用域

        2、函数名称相同

        3、函数参数类型不同或者个数不同或者顺序不同

        4、返回值不能作为重载条件

#include<iostream>using namespace std;//作用让函数名相同,提高复用性
void func1(){}
void func1(int a){}
//引用作为重载条件
void func2(int &a){}
void func2(const int &a){}//函数重载碰到默认参数 语法通过调用传入1参数会产生二义性
void func3(int a){}
void func3(int a,int b=10){}
int main(){int a=10;func2(a);//第一个 因为a是可读可写的func2(10);//第二个 有const合法}

三、指针 

 1、指针的定义和使用

1、作用:可以通过指针间接访问内存

        (1)内存编号是从0开始记录的,一般用16进制表示

        (2)可以通过指针变量保存地址

    启动一个程序,系统会给其分配内存空间,内存空间最小1字节,内存中每一个字节都有编号,这个编号称为内存的地址

        

2、定义与使用

#include <iostream>
using namespace std;int main(){//定义指针int a=10;int* p;p=&a;//指针就是地址cout << "a的地址" << &a <<endl;cout << "指针p为" << p << endl; //0xABCD//使用指针//可以通过解引用(*)的方式来找到指针指向的内存*p=100;cout << "a=" << a <<endl;//100return 0;
}

2、 指针所占的内存空间

32位操作系统指针占用4字节,64位8个字节

3、空指针与野指针

1、空指针

      指针变量指向内存中编号为0的空间

      用途:用于初始化指针变量

      注意:空指针指向的内存空间不可以访问

      内存编号0~255为系统占用内存,不允许用户访问

#include <iostream>
using namespace std;int main(){//空指针:用于给指针变量初始化,不初始化就不知道指向哪里int *p=NULL;//语法通过,但是不可以访问,0~255之间内存编号不允许访问*p=100;return 0;
}

2、野指针  

        指针变量指向非法的内存空间,不是我们申请的空间

#include <iostream>
using namespace std;int main(){//指针指向内存地址编号为0x1100的空间int* p=(int*)0x1100;//访问野指针出错cout<< *p <<endl;return 0;}

3、万能指针

        不可以定义void类型的变量,因为编译器不知道给变量分配多大的空间,但是可以定义void *类型,因为指针都是4个字节

int main()
{int a = 10;void *p = (void *)&a;// printf("%d\n", *p);//err  p是void*,不知道取几个字节的大小printf("%d\n", *(int *)p2); // 10}

4、const修饰指针

const修饰指针:常量指针

        int a=10;

        const int* p=&a;(const修饰的是int* 所以值不能改)

        指针的指向可以改,但该指针指向的值不可以改

const修饰常量:指针常量

        int a=10;

        int* const p=&a;(const修饰的是p 所以地址不能改)

        const后面跟的p是变量,修饰后成为常量

        指针的指向不能改,但指向的值可以改

const即修饰指针又修饰常量

        const int* const p=&a;

5、指针和数组

int a[5];

数组名a代表数组,也代表第0个元素地址

在数值上:a==&a[0]==&a==地址

若&a[0]=0x0001,则&a[0]+1=0x0005(元素地址+1=跨过一个元素),等同于a+1

&a+1跨过整个数组==0x0021

void array()
{int a[5];/*数组名a代表数组,也代表第0个元素地址a==&a[0]==&a==地址若&a[0]==01,则&a[0]+1=05(元素地址+1跨过一个元素),等同于a+1&a+1跨过整个数组=21*/printf("%d\n", a);printf("%d\n", &a);printf("%d\n", &a[0]);printf("%d\n", &a[0] + 1);printf("%d\n", &a[1]);printf("%d\n", a + 1);printf("%d\n", &a + 1);printf("%d\n", &a[4] + 1);
}

int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

a[0][0]第0行第0个元素

&a[0][0]第0行第0个元素地址  +1跨过一个元素
a[0]第0行一维数组名 +1跨过一个元素
&a[0]第0行地址  +1跨过一行
a二维数组名,首行地址&a[0]  +1跨过一行
&a二维数组地址  +1跨过整个数组

  //元素个数
   int num=sizeof(a)/sizeof(a[0][0]);
   //行数 总大小/一行的大小
   int row=sizeof(a)/sizeof(a[0]);
   //列数 行大小/一个元素大小
   int column=sizeof(a[0])/sizeof(a[0][0]);

6、指针和函数
void swap(int *p1,int *p2){int temp=*p1;*p1=*p2;*p2=temp;
}
int main(){int a=10;int b=20;swap(&a,&b);
}
void bubbleSort(int * arr,int len){//int *arr可以写成int arr[]for(int i=0;i<len-1;i++){for(int j=0;j<len-i-1;j++){if(arr[j]>arr[j+1]){int temp=arr[j];arr[j]=arr[j+1];arr[j+1]=temp;}}}
}
#include <stdio.h>int add(int a, int b)
{return a + b;
}
int subtract(int a, int b)
{return a - b;
}int main()
{int num = 10;int *pNum = &num;// 基本指针类型printf("%d\n", *pNum); // 10printf("%p\n", &pNum);printf("%p\n", pNum); // 保存的num的地址,和&num一样printf("%p", &num);// 函数指针int (*operation)(int, int);operation=add;printf("a+b=%d\n",operation(5,3));return 0;
}
/*** 指针==地址==编号* 指针变量:存放指针(地址)的变量* 指针:*  基本指针类型*      int* char* float* double* void*可以指向任何类型数据,使用前需类型转换*  指向指针的指针*      int** 指向int*的指针,即指针的指针,用于动态数组等场景*  函数指针:指向这个函数*      返回值类型 (*函数指针名)(参数类型)*  在使用时,对一个表达式取*,就会对表达式减一级*,如果对表达式取&,就会加一级**  32位操作系统中,指针大小4字节,64位8字节****/

四、结构体

1、定义

struct 类型名称{

        成员列表

};

struct Student {int age;string name;
}s3;
int main(){
//1、struct Student s1其中struct可以省略struct Student s1;s1.name="张三";s1.age=18;
//2、struct Student s2={...}struct Student s2={18,"张三"}
//3、定义时顺便赋值s3s3.age=20;s3.name="王五";
}

2、结构体数组

struct 数组名[个数]={{},{},{}...}

struct Student{string name;int age;
};int main(){struct Student stuArr[2]={{"张三",18},{"王五",20}};stuArr[1].name="张武";
}

3、结构体指针

->

struct Student{string name;int age;
};
int main(){Student s={"张三",18};Student* p=&s;p->name="王五";
}

4、结构体嵌套结构体
struct student{string name;int age;
};
struct teacher{string name;int age;struct student stu;
};
int main(){teacher t;t.stu.name="小小";}

5、结构体做函数参数
struct Student {int age;string name;
};
void printStu1(struct Student s){}
void printStu2(struct Student *p){p->name="李四";p->age=32;
}int main(){struct Student s;s.name="张三";s.age=18;
//值传递printStu1(s);
//址传递printStu2(&s)
}

如果不想修改主函数中的数据,用值传递,反之用地址传递

6、结构体中const使用

作用:用const防止误操作

struct student{string name;int age;
};
void printStu(const student * s){}
int main(){student s={"张三",20};printStu(&s);
}

值传递会复制副本,所以速度慢

7、案例
struct Student{string name;int age;
};
struct Teacher{string name;struct Student arr[5];
};void allocateSpace(struct Teacher arr[],int len){string nameSeed="ABCDE";for(int i=0;i<len;i++){arr[i].name="Teacher_";arr[i].name+=nameSeed[i];for(int j=0;j<5;j++){arr[i][j].name="Student_";arr[i][j].name+=nameSeed[j];arr[i][j].age=18;}}
}
int main(){Teacher tArr[3];int len=sizeof(tArr)/sizeof(tArr[0]);allocateSpace(tArr,len);    }

第二部分 核心编程

一、内存模型

C++程序执行时,将内存大致分为4个区域

 代码区:存放函数体的二进制代码,由操作系统进行管理

 全局区:存放全局变量和静态变量以及常量

 栈区:由编译器进行自动分配与释放,存放函数的参数值,局部变量

 堆区:由程序员分配和释放,若不释放,程序结束由操作系统回收

1、程序运行前 

在程序编译后,生成exe可执行程序,未执行程序前分为两个区域

    代码区

        存放CPU执行的机器指令

        代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可

        代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令

   全局区

        全局变量静态变量(static)

        包含了常量区:字符串常量和其他常量(const修饰的全局变量)也存放在此

        该区域数据在程序结束后由操作系统释放

  注意:局部变量和局部常量不在全局区

2、 程序运行后

分为栈区和堆区

    栈区

        由编译器自动分配释放,存放函数参数值,局部变量

        注意:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

   堆区

        new

        delete 

        释放数组delete[] arr;

二、引用

1、引用的基本使用

作用:给变量起别名

语法:数据类型 &别名=原名

2、引用的注意事项

引用必须初始化

初始化后不可改变

int main(){int a=10;int &b=a;
}
3、引用做函数参数
void swap(int &a,int &b){
//形参中的a是main中a的别名int temp=a;b=a;a=temp;
}
int main(){int a=20;int b=20;swap(a,b);
}

4、引用做函数返回值

不要返回局部变量的引用

函数的调用可以作为左值

int& func(){static int a=10;return a;
}
int main(){int &ref=func();//相当于int* const ref=&aref=20;//内部发现ref为引用,自动转为*ref=20test()=1000;
}

5、引用的本质

引用的本质是一个指针常量(指针指向不可变)

6、常量引用

作用:常量引用主要用来修饰形参,防止形参改变实参

void showValue(const int& v){//v+=10;
}
int main(){int a=10;showVlue(a);//int& ref=10;引用本身需要一个合法的内存空间,因此错误const int& ref=10;//加上const之后,编译器将代码进行修改 int temp=10;const int& ref=temp;//ref=20;加入const之后成为只读,不可修改
}

三、类和对象

1、封装
#include<iostream>
#include<string>using namespace std;class Student{
public://成员属性/成员变量string m_Name;int m_Id;//成员函数/成员方法void show(){}void set(string name,int id){this->m_Name=name;this->m_Id=id;}
};
int main(){Student s1;s1.m_Name="张三";s1.m_Id=1;s1.show();
}

权限

        public 公共权限         成员类内可以访问,类外可以访问

        protected 保护权限   成员类内可以访问,类外不可以访问 继承子可以访问父保护内容

        private 私有权限       成员类内可以访问,类外不能访问

 struct和class的区别为:默认访问权限不同

        struct:public

        class:private

2、对象的初始化和清理
2.1 构造与析构

构造函数

        有参构造/无参构造

        普通构造/拷贝构造

析构函数:不可以有参数,不能重载

#include<iostream>using namespace std;class Person{
public:Person(){//没有返回值 函数名和类名相同 可以有参数,可以重载  创建对象时,会自动调用一次 默认提供}Person(string name,int age){this->name=name;thia->age=age;}//拷贝构造Person(const Person &p){this->age=p.age;this->name=p.name;}~Person(){//没有返回值 函数名和类名相同 不可以有参数 对象销毁前,会自动调用一次 默认提供}private:string name;int age;
};
int main(){//构造函数调用方式1Person p1;//栈上的数据,main执行完毕会释放Person p2("li",10);Person p3(p2);//不会创建对象,编译器会认为是函数的声明Person p4();//构造函数调用方式2Person p5=Person("zhang",30);//匿名对象 当程序执行结束后,系统立刻回收(这行代码执行后回收)Person("zhang",30);//不能利用拷贝构造函数初始化匿名对象,编译器会任务Person(p3) === Person p3; 对象声明重定义构造函数调用方式3    隐式调用-拷贝构造Person p6=p3;
}
2.2 拷贝构造调用时机 

拷贝构造函数调用时机

        使用一个已经创建完毕的对象来初始化一个新对象

        值传递的方式给函数参数传值

        以值方式返回局部对象 

#include<iostream>using namespace std;class Pereson{
public:Person (){}Person(int age){this->age=age;}Person(const Person &p){this->age=p.age;}~Person(){}private:int age;
};
void doWork1(Person p){
//值传递的本质会拷贝出一个临时副本出来
}
Person doWork2(){Person p1;//会根据p1拷贝出一个新的对象返回return p1;
}
//拷贝构造函数调用时机
void demo1(){//1、使用一个已经创建完毕的对象来初始化一个新对象Person p1(10);Person p2(p1);//2、值传递的方式给函数参数传值Person p3;doWork1(p3);//3、值传递返回局部对象Person p4=doWork2();
}
2.3 构造函数调用规则 

默认情况下,C++编译器至少给一个类添加3个函数

        1、默认构造

        2、默认析构

        3、默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则

        如果用户定义有参构造,c++不再提供默认无参构造,但是会提供默认拷贝构造

        如果用户定义拷贝构造函数,c++不会再提供其他构造函数

2.4 深拷贝与浅拷贝 

深拷贝与浅拷贝 

        浅拷贝:简单的赋值拷贝操作

        深拷贝:在堆区重新申请空间,进行拷贝操作 

总结:如果属性有在堆区开辟内存的,一定要提供拷贝构造函数,防止浅拷贝带来的问题

#include<iostream>using namespace std;class Person{
public:Person(){}Person(int age,int height){this->age=age;//堆区创建的返回就是int*this->height=new int(height);}~Person(){if(height!=NULL){delete height;height=NULL;}}
private:int age;int *height;
};void demo1(){Person p1(18,160);//报错 Person p2(p1);
/*
原因:创建了p1,p2两个对象
如果利用编译器提供的拷贝构造函数,会做浅拷贝操作
堆区开辟的空间,浅拷贝将地址拷贝
执行析构时p2先被释放,同时释放堆区内存,p1在释放就会出错
浅拷贝的问题就是堆区内存重复释放,使用深拷贝解决*/}
#include<iostream>using namespace std;class Person{
public:Person(){}Person(int age,int height){this->age=age;//堆区创建的返回就是int*this->height=new int(height);}Person(const Person &p){//深拷贝this->age=age;this->height=new int(*p.height); }~Person(){if(height!=NULL){delete height;height=NULL;}}
private:int age;int *height;
};void demo1(){Person p1(18,160);Person p2(p1);}
2.5 初始化列表 

初始化列表 

#include <iostream>using namespace std;class Person{
public://初始化列表初始化Person():age(10),height(20){}Person(int a,int h):age(a),height(h){}private:int age;int height;
};

2.6 类对象作为类成员 

类对象作为类成员

#include<iostream>using namespace std;class A{};class B{
public:A a;
};void demo1(){B b;    
}
//先有A再有B

当其他类对象作为本类成员,构造时候先构建类对象,在构造自身,先析构本身,在析构类对象

2.7 静态成员 

静态成员 

        静态成员就是在成员变量和成员函数前加上关键字static

  静态成员变量

        所有对象共享同一份数据

        在编译阶段分配内存

        类内声明,类外初始化

  静态成员函数

        所有对象共享同一个函数

        静态成员函数只能访问静态成员变量

#include<iostream>using namespace std;class A{
public:static int m_A;static void func(){}
};int A::m_A=100;
//静态成员变量,不属于某个对象,所有对象共享同一份数据
//因此,两种访问方式:通过对象访问/通过类名访问
void demo1(){A a1;//a1.m_A=200;A a2;a2.m_A=200;//A::m_A=300;
}

3、c++对象模型和this
3.1 成员变量和成员函数

成员变量和成员函数分开存储

#include<iostream>using namespace std;/*
变量只能存储在 min(他的长度,pack参数)的整数倍地址上char 地址为1的倍数short  2的倍数 0 2 4 8int    4的倍数 0 4 8 12double 8的倍数 0 8 16 24
结构体整体对齐跟他的 min(最长的字段,pack)整数倍对齐
数组按照数组类型来对齐
结构体嵌套结构体按照被嵌套的最大元素长度对齐
*/struct AA{long long a; //8char b; //int c;  //和12对齐char d[2]; //偏移量目前16+2+(6)=8*3}
struct BB{long long a;//8char b;//1+7struct AA c;//按8*2对齐char d[2];//偏移量16+24+2+(6)=8*6
}/*#pragma pack(show) 默认16
//16一般超过结构体中最大大小,所以没影响
如果
#pragma pack(2)
那么int地址 0 2 4 6struct CC{long long a; //8char b; //2int c;  //和10对齐char d[2]; //偏移量目前14+2=2*8}*/
#include<iostream>class A{};
class Person{
public:int a;//非静态成员变量 属于类的对象 4字节static int b; //静态成员变量 不属于类对象void func1(){}//非静态成员函数 不属于类的对象static void func2(){}//静态成员函数 不属于类的对象};
int Person::b;void demo1(){
//空对象占用内存空间sizeof(a) 为1字节A a;
//C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占用内存的位置 每个空对象也应该有一个独一无二的内存地址}
void demo2(){Person p;//sizeof(p); 4字节 只有非静态成员变量属于类的对象}
3.2 this指针 

 this指针

        成员变量和成员函数分开存储

        每个非静态成员函数只会诞生一份函数实例,也就是多个同类型的对象会公用一块代码

        this指针指向被调用的成员函数所属的对象

this指针的用途

        当形参和成员变量同名时,使用this区分

        在类的非静态成员函数中返回对象本身,可使用return *this,this指针指向对象,解引用返回对象本身

#include<iostream>using namespace std;class Person{
public://如果返回值写Person 会调用拷贝构造函数返回新的对象 Person& PersonAddAge(Person &p){this->age+=p.age;return *this;}int age;};
void demo1(){Person p1;p1.age=10;Person p2;p2.age=10;p2.PersonAddAge(p1).PersonAddAge(p2);//30
}

空指针可以访问成员函数,但要注意this,空指针不能访问属性

3.3 常函数与常对象 

const修饰成员函数

        成员函数加const成为常函数

        常函数不可以修改成员属性

        成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象

        声明对象前加const称为常对象

        常对象只能调用常函数

#include <iostream>using namespace std;class Person{
public://this指针的本质 指针常量 指针的指向不可以改变//加const 本质修饰的this指针,让指针指向的值不可以改变void show() const{//this->a=100;this->b=10;}int a;mutable int b;//在常函数中也想修改
};void demo2(){const Person p;//常对象,属性不可以修改,只能调用常函数,不能调用普通函数,因为普通函数可以修改属性//p.a=100;p.b=100;
}

4、友元

友元:让一个函数或者类,访问另一个类中私有成员

全局函数做友元

   

#include<iostream>using namespace std;class Building{
//goodGuy可以访问私有成员了
friend void goodGuy(Building *building);
public:Building(){this->sittingRoom="客厅";this->bedRoom="卧室;"}
public:string sittingRoom;
private;string bedRoom;};
//全局函数
void goodGuy(Building *building){println(building->sittingRoom);println(building->bedRoom);
}
void demo1(){Building building;goodGuy(&building);}

 类做友元

#include<iostream>using namespace std;//声明
class Building;class Building{
//GoodGuye类可以访问私有成员了
friend class GoodGuy;
public:Building();
public:string sittingRoom;
private;string bedRoom;};
//类外可以初始化成员函数
Building::Building(){this->sittingRoom="客厅";this->bedRoom="卧室;"
}class GoodGuy{
public:GoodGuy(){building=new Building;}void visit();    Building *building;
};void GoodGuy::visit(){println(building->sittingRoom);println(building->bedRoom);
}void demo1(){GoodGuy goodGuy;goodGuy.visit();}

成员函数做友元  

#include<iostream>using namespace std;class Building{
//visit方法可以访问私有成员了
friend void GoodGuy::visit();    
public:Building();
public:string sittingRoom;
private;string bedRoom;};
//类外可以初始化成员函数
Building::Building(){this->sittingRoom="客厅";this->bedRoom="卧室;"
}class GoodGuy{
public:GoodGuy();void visit();    Building *building;
};GoodGuy::GoodGuy(){buildint=new Building;
}
void GoodGuy::visit(){println(building->sittingRoom);println(building->bedRoom);
}void demo1(){GoodGuy goodGuy;goodGuy.visit();}

5、运算符重载

运算符重载:对已有运算符重新进行定义,赋予另一种功能,以适应不同数据类型

加号运算符重载

#include<iostream>using namespace std;//1、成员函数重载+
class Person{
public:int a;int b;Person operator+(const Person &p){Person temp;temp.a=this->a+p.a;temp.b=this->b+p.b;return temp;}
};
//2、全局函数重载+
Person operator+(const Person &p1,const Person &p2){Person temp;temp.a=p1.a+p2.a;temp.b=p1.b+p2.b;return temp;
}void demo1(){Person p1;p1.a=10;p1.b=20;Perosn p2;p2.a=30;p2.b=40;Person p3=p1+p2;//本质调用 Person p3=p1.operator+(p2)//Person p3=operator+(p1,p2)//p3.a==40;p3.b==60}

   

#include <iostream>using namespace std;class Person{
friend ostream& opetator<<(ostream &cout,Perosn &p);
private:int a ; int b;
};
ostream& opetator<<(ostream &cout,Perosn &p){cout<<"a="<<p.a<<" b="<<p.b;return cout;
}

     

class MyInteger {friend ostream& operator<<(ostream& out, MyInteger myint);public:MyInteger() {m_Num = 0;}//前置++MyInteger& operator++() {//先++m_Num++;//再返回return *this;}//后置++MyInteger operator++(int) {//先返回MyInteger temp = *this; //记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++;m_Num++;return temp;}private:int m_Num;
};ostream& operator<<(ostream& out, MyInteger myint) {out << myint.m_Num;return out;
}//前置++ 先++ 再返回
void test01() {MyInteger myInt;cout << ++myInt << endl;cout << myInt << endl;
}//后置++ 先返回 再++
void test02() {MyInteger myInt;cout << myInt++ << endl;cout << myInt << endl;
}
class Person
{
public:Person(int age){//将年龄数据开辟到堆区m_Age = new int(age);}//重载赋值运算符 Person& operator=(Person &p){if (m_Age != NULL){delete m_Age;m_Age = NULL;}//编译器提供的代码是浅拷贝//m_Age = p.m_Age;//提供深拷贝 解决浅拷贝的问题m_Age = new int(*p.m_Age);//返回自身return *this;}~Person(){if (m_Age != NULL){delete m_Age;m_Age = NULL;}}//年龄的指针int *m_Age;};
class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;};bool operator==(Person & p){if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){return true;}else{return false;}}bool operator!=(Person & p){if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){return false;}else{return true;}}string m_Name;int m_Age;
};void test01()
{//int a = 0;//int b = 0;Person a("孙悟空", 18);Person b("孙悟空", 18);if (a == b){cout << "a和b相等" << endl;}else{cout << "a和b不相等" << endl;}if (a != b){cout << "a和b不相等" << endl;}else{cout << "a和b相等" << endl;}
}
class MyPrint
{
public:void operator()(string text){cout << text << endl;}};
void test01()
{//重载的()操作符 也称为仿函数MyPrint myFunc;myFunc("hello world");
}class MyAdd
{
public:int operator()(int v1, int v2){return v1 + v2;}
};void test02()
{MyAdd add;int ret = add(10, 10);cout << "ret = " << ret << endl;//匿名对象调用  cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
}

6、继承
6.1 基本语法
class Base{};class A:public Base{}

class 子类(派生类): 继承方式  父类(基类)

6.2  继承方式

公共继承

保护基础

私有继承

缩小了权限

6.3 继承中的对象模型

父类中所有非静态成员属性都会被子类继承下去

6.4 继承中构造和析构顺序

父类构造--子类构造--子类析构--父类析构

6.5 继承同名成员处理方式

当子类与父类出现同名的成员的成员,如何通过子类对象,访问子类或父类同名数据

        访问子类同名成员 直接访问即可

        访问父类同名成员 需要加作用域

6.6 继承同名静态成员处理方式

静态成员和非静态成员出现同名

        访问子类同名成员 直接访问即可

        访问父类同名成员 需要加作用域

class Base {
public:static void func(){cout << "Base - static void func()" << endl;}static void func(int a){cout << "Base - static void func(int a)" << endl;}static int m_A;
};int Base::m_A = 100;class Son : public Base {
public:static void func(){cout << "Son - static void func()" << endl;}static int m_A;
};int Son::m_A = 200;//同名成员属性
void test01()
{//通过对象访问cout << "通过对象访问: " << endl;Son s;cout << "Son  下 m_A = " << s.m_A << endl;cout << "Base 下 m_A = " << s.Base::m_A << endl;//通过类名访问cout << "通过类名访问: " << endl;cout << "Son  下 m_A = " << Son::m_A << endl;cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}//同名成员函数
void test02()
{//通过对象访问cout << "通过对象访问: " << endl;Son s;s.func();s.Base::func();cout << "通过类名访问: " << endl;Son::func();Son::Base::func();//出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问Son::Base::func(100);
}

6.7 多继承

class 子类:继承方式 父类1,继承方式 父类2

6.8 菱形继承

菱形继承:

       两个派生类继承同一个基类

        某个类同时继承两个派生类

 cl /d1 reportSingleClassLayout类 file.cpp 

class Animal
{
public:int m_Age;
};//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo   : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};void test01()
{SheepTuo st;st.Sheep::m_Age = 100;st.Tuo::m_Age = 200;cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;cout << "st.m_Age = " << st.m_Age << endl;
}
  • 虚继承可以解决菱形继承问题
  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义利用
7、多态
7.1 基本概念
#include<iostream>
using namespace std;class Animal{
public:
//如果想执行让猫说话,那函数地址就不能提前绑定需要在运行期间绑定--地址晚绑定void speak(){//virtual void speak(){println("dongwu");}};
class Cat :public Animal{
public:void speak(){println("miao");}
};
class Dog:public Animal{
public:void speak(){println("wang");}
};
void doSpeak(Animal &animal){animal.speak();
}void demo1(){Cat cat;doSpeak(cat);//执行的时动物说话,因为地址早绑定,在编译阶段就确定了函数地址}

动态多态满足条件

        有继承关系

        子类重写父类虚函数

        父类的指针/引用 指向子类的对象

7.2 多态剖析

写了一个虚函数virtual void speak(),类的内部发生了改变,多了一个虚函数指针(vfptr),指向虚函数表(vftable),表中记录着 虚函数的入口地址,当子类继承了父类,同样会继承父类结构,当子类重写父类虚函数,会将自身虚函数表中虚函数入口地址替换为子类虚函数地址。所以当父类指针或引用指向子类对象时,发生多态

7.3 纯虚函数和抽象类

当类中有了纯虚函数,这个类称为抽象类

        无法实例化对象

        子类必须重写抽象类中纯虚函数,否则也属于抽象类

class Base
{
public://纯虚函数//类中只要有一个纯虚函数就称为抽象类//抽象类无法实例化对象//子类必须重写父类中的纯虚函数,否则也属于抽象类virtual void func() = 0;
};class Son :public Base
{
public:virtual void func() {cout << "func调用" << endl;};
};void test01()
{Base * base = NULL;//base = new Base; // 错误,抽象类无法实例化对象base = new Son;base->func();delete base;//记得销毁
}

7.4 饮品
#include <iostream>using namespace std;class AbstractDrink{
public://煮水virtual void Boil()=0;//冲泡virtual void Brew()=0;//倒杯virtual void PourInCup()=0;//加入佐料virtual void PutSomething()=0;//制作饮品void makeDrink(){Boil();Brew();PourInCup();PutSomething();}
};
Class Coffee :public AbstractDrink{void Boil(){}void Brew(){}void PourInCup(){}void PutSomething(){}};
void doWork(AbstractDrink *drink){drink->makeDrink();delete drink;
}
void demo1(){doWork(new Coffee);}

7.5 虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类析构

解决:将父类中析构函数改为虚析构或纯虚析构

#include<iostream>
using namespace std;class Animal{
public:virtual void speak()=0;virtual ~Animal(){}
};
class Cat :public Animal{
public:Cat(string name){this->name=new string(name);}void speak(){}string *name;~Cat(){if (this->name!=NULL){delete this->name;this->name=NULL;}}
};
void demo1(){Animal *animal=new Cat("加菲");animal->speak();//父类指针在析构的时候,不会调用子类中析构,导致子类中如果有堆区数据,造成内存泄漏delete animal;
}

1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构

3. 拥有纯虚析构函数的类也属于抽象类

7.6 电脑组装
#include<iostream>using namespace std;class CPU{
public:virtual void calculate()=0;  
};
class VideoCard{
public:virtual void display()=0;  
};
class Memory{
public:virtual void storage()=0;
};
class IntelCpu:public CPU{void calculate(){}
};
class IntelVideoCard:public VideoCard{void display(){}
};
class IntelMemory:public Memory{void storage(){}
};
class Computer{
public:Computer(CPU *cpu,VideoCard *vc,Memory *mem){this->cpu=cpu;this->vc=vc;this->mem=mem;}void doWork(){cpu->calculate();vc->display();mem->storage();}~Computer(){if(this->cpu!=NULL){delete this->CPU;this->cpu=NULL;}if(this->vc!=NULL){delete this->vc;this->vc=NULL;}if(this->mem!=NULL){delete this->mem;this->mem=NULL;}}
private:CPU *cpu;VideoCard *vc;Memory *mem;
};
void demo1(){Computer * com=new Computer(new InterCPU,new InterVideoCard,new InterMemory);com->doWork();delete com;
}

8、文件操作
8.1 文本文件

头文件<fstream>

文本文件:文本以文本的ASCII码的形式存储

二进制文件:以二进制的形式存储

三大类:

        ofstream:写操作

        ifstream:读操作

        fstream:读写

 文本文件写文件

        1、包含头文件

                #include<fstream>

        2、创建流对象

                ofstream ofs;

        3、打开文件

                ofs.open("路径",打开方式);

        4、写数据

                ofs<<"写入的数据";

        5、关闭文件

                ofs.close();

文件打开方式

打开方式解释
ios::in为读文件而打开文件
ios:out写文件
ios:ate初始位置:文件尾
ios:app追加方式写文件
ios:trunc如果文件存在先删除,在创建
ios::binary二进制方式

注意:文件打开方式可以配合使用,利用|操作符

#include <fstream>void test01()
{ofstream ofs;ofs.open("test.txt", ios::out);ofs << "姓名:张三" << endl;ofs << "性别:男" << endl;ofs << "年龄:18" << endl;ofs.close();
}

文本文件读文件

 

#include<iostream>
using namespace std;#include<fstream>void demo1(){ifstream ifs;ifs.open("test.txt",ios::in);if(!ifs.is_open()){return;}//1、字符数组char buf[1024]={0}; while(ifs>>buf){cout<<buf<<endl;}   //2、字符数组char buf[1024]={0}; while(ifs.getline(buf,sizeof(buf))){cout<<buf<<endl;}//3、stringstring buf;while(getline(ifs,buf)){cout<<buf<<endl;}//4、char cchar c;while((c=ifs.get())!=EOF){/EOF end of filecout<<c;}ifs.close();}

8.2 二进制文件

写文件

#include<iostream>
using namespace std;
#include <fstream>class Person{
public:char m_Name[64];int m_Age;
};void demo1(){ofstream ofs;ofs.open("person.txt",ios::out|ios::binary);Person p={"张三",18};ofs.write((const char*)&p,sizeof(Person));ofs.close();
}

 读文件

#include<iostream>
using namespace std;
#include <fstream>class Person{
public:char m_Name[64];int m_Age;
};void demo1(){ifstream ifs;ofs.open("person.txt",ios::in|ios::binary);if(!ifs.is_open()){return; }Person p;ifs.read((char*)&p,sizeof(Person));//p.m_Name p.a_Ageofs.close();
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com