文章目录
C++特点
封装、继承、多态
一、命名空间
命名冲突的解决
C语言中
1、c语言中解决命名冲突的方法:使用static关键字
- 作用:
修饰局部变量:延长局部变量的生命周期,程序结束后再释放
修饰全局变量:该全局变量只能在本文件访问,不能在其他文件访问
修饰函数:该函数只能在本文件调用,不能在其他文件调用
#include <iostream>
int count = 4;
int count = 5;
int main()
{
return 0;
}
报错出现重复定义
C++中命名冲突的解决:命名空间
命名空间的定义
namespace 名字{变量、函数}
命名空间的使用
1、使用作用域限定符:使用命名空间的名字::变量名或者函数名
2、使用using关键字导入命名空间:using namespace 命名空间;using 命名空间::变量名或者函数名
eg:
#include <iostream>
namespace myspcaeA//解决命名冲突问题
{
int count = 4;
int index = 0;
}
namespace myspaceB
{
int count = 5;
int index = 10;
}
int count = 6;//默认全局的变量
int index = 20;
int main()
{
printf("count = %d\n",count);//默认输出全局变量,输出6
printf(“count = %d\n”,myspaceA::count);
printf(“count = %d\n”,myspaceB::count);
printf("count = %d\n",index);//默认输出全局变量,输出6
printf(“count = %d\n”,myspaceA::index);
printf(“count = %d\n”,myspaceB::index);
return 0;
}
想要输出myspaceA里面的内容
引入运算符: ::作用域限定符
namespace myspaceB
{
int count = 5;
int index = 10;
namespace myspaceBB//可以嵌套使用
{
int count = 7;
}
}
printf(“count = %d\n”,myspaceB::myspaceBB::count);//输出格式
//访问myspaceC中的数
namespace myspaceC
{
int max = 10;
int min = 10;
}
using namespace myspaceC;//将myspaceC导成全局变量
//using namespace myspaceA;
//using namespace myspaceB;//尽量少用using 导入命名空间,因为会导致命名冲突
//输出的话会再次出现命名冲突
printf("max = %d\n", max);//输出格式
以下情况
1、不同源文件可以有相同的命名空间
2、使用命名空间加作用域限定符访问成员时,优先导入本文件的命名空间
3、使用using导入命名空间时,导入的是所以程序中的命名空间(只要没有命名冲突都是被允许的)(直接导入)
防止头文件重复的两种方法
命名空间尽量定义在.h文件
//防止头文件重复包含的两种方法
//1、用宏
//2、#pragm once 预处理指令,z只编译一次(大部分使用)
//#ifndef __NAMESPACE_H_
//#define __NAMESPACE_H_//宏
#pragm once //预处理指令,z只编译一次
.
.
.
//#endif
std的介绍和使用
#include <iostrenm>
//std:包含升级之后的功能
int main()
{
std::cout << "hello world" << tsd::endl;
//std:标准库命名空间
}
//简化
#include<iostream>
using namespace std;//此命名空间存放的是C++相对于C升级的功能(函数、库文件、变量)
int main()
{
cout << "hello world" << endl;
}
命名空间注意事项
(1)不能访问其他源文件的命名空间
(2)当多个源文件的命名空间名字相同时:如果没有出现重复的变量名或者函数命名,系统会合并两个命名空间(但是实际上并不是合成一个命名空间,只是能通过编译);否则会报错
二、输入与输出
#include<iostream>
using namespace std;
int main()
{
//cout printf 缓冲区
const char *ptr = "hello world";
char temp = 'X';
int num = 6;
double b = 1.342;
cout << "hello world" << endl;
cout << "ptr =" << ptr << endl;
cout << "temp = " << temp << endl;
cout << "num = " << num << endl;
cout << "b = " << b << endl;
cout << " &num = "<< &num << endl;
printf("ptr = %p\n", ptr);
}
printf、cout缓冲区问题
printf
/*不能输出*/
#include <stdio.h>
int main()
{
printf("hello world!");
//scanf
//flush;刷新缓冲区
//while(1);也能输出,每次程序执行完后,清空缓冲区
return 0;
}
printf:行缓冲:满一行输出或遇到‘\n’
使用printf调试时,一定要加’\n’或手动刷新缓冲区
scanf:输入前,刷新缓冲区
cout
#include <iostream>
using namespace std;
int main()
{
cout << "hello worlld!" << endl;
// cout << "hello worlld!\n"
//endl相当于刷新,输入'\n'
while(1)
;
return 0;
}
cin输入
#include <iostream>
using namespace std;
int main()
{
int num;
char str[100];
double b;
// //cin :输入流 cunt:输出流 cerr 错物流 clog 日志流 对象
//流运算符: >>输入 <<输出
cout << "Please input num:";
cin >> num;
//getchar();
cin.get();
cout << "Please input str:";
//cin >> str;
cin.getline(str,100);
//getline()
cout << "Please input b:";
cin >> b;
cout << num << endl;
cout << str << endl;
cout << b << endl;
}
输入垃圾问题
缓冲区垃圾问题
getchar();
cin.get();
cerr错误流
#include <iostream>
using namespace std;
int main()
{
cerr << "hello world!" << endl;//dup dup2重定向到文件或某个远程socket里
//cerr没有缓冲区
clog << "hello world!" << endl;
//clog有缓冲区
return 0;
}
重定向
#include <stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
//0标准输入 1标准输出 2标准出错
int fd = open ("hello.txt", O_RDWR | O_CREAT, 0655);
dup2(fd, STDOUT_FILENO);//输出重定向到fd上
dup2(fd, STDIN_FILENO);//输入重定向 从文件里面读
dup2(fd, STDERR_FILENO);//错误信息重定向到fd
//sockfd = socket confd;
//dup2(STDOUT_FILENO,cond);
char src[100];
read(STDIN_FILENO, src, sizeof(src));//从键盘上读
printf("src = %s\n",src);
write(STDERR_FILENO, "hello world", 11);//输出到终端上
return 0;
}
#include <iostream>
#include<stdio.h>
using namespace std;//标准库命名空间
int main()
{
int num;
char ch;
//输入垃圾问题
printf("Please input num:\n");
scanf("%d",&num);
getchar();
printf("Please input ch:\n");
scanf("%c",&ch);
//cin :输入流 cunt:输出流 cerr 错物流 clog 日志流 对象
//流运算符: >>输入 <<输出
cout << "hello world" << endl;
cout << "num = " <<num<< endl;
cout << "num = " << ch << endl;
printf("num = %d ch = %s\n",num,ch);
}
三、C++对C的升级
基本语句的升级
for语句
优化循环变量的作用域
#include <stdio.h>
int main()
{
int i;
for(i = 0; i<=100; i++)
{
}
//只有for循环结束后空间才能释放,浪费资源
return 0;
}
#include<iostream>
using namespace std;
int main()
{
for(int i = 0; i < 100; i++) //节省空间,在C语言中也支持
{
}
//freach
int array[5] = {1,2,3,4,5} ;
for(int temp : array)
{
cout << temp << endl;
}
char src[100] = "hello world";
for(char temp : src)
{
if(temp != '\0')
{
cout << temp << endl;
}
else
{
break;
}
}
return 0;
}
条件表达式:可以做左值
关键字的升级register
程序的存储位置
C中register的使用
作用
提高程序运行效率(省去CPU从内存抓取数据的开销)
语法作用
尽可能将变量保存在CPU内部寄存器中
使用注意事项
1、只能修饰局部,不能修饰全局变量和函数
2、register修饰变量,不能通过&获取该变量的地址(有可能该变量保存在CPU内部寄存器中,&获取的是内存里的)
3、register修饰变量类型一定是CPU所能处理的数据类型(有的CPU不支持浮点型运算)
什么时候用register修饰一个变量
频繁访问的变量
volatile
作用
防止编译器优化(将变量优化到寄存器中(寄存器存在边际效应))
使用场景
访问硬件时,所使用的全局变量
volatile int count = 5;
volatile register int index;//提醒检查寄存器的变量是否有变化
C++中对register的升级
优化内容
当对register变量取地址时,会将该变量重新保存到内存中
#include<iosteram>
using namespace std;
int main()
{
register int num = 5;
printf("&num = %p\n",&num);
return 0;
}
const关键字的升级:const修饰的是变量
地址之间的赋值必须是const对const
const:修饰变量、只读变量
const int num = 5;
const int *p_num = &num ;
关键字的升级auto
C语言中
自动变量,离开作用域时自动释放
只是用来说明该变量的作用域,相当于局部变量
C++中
auto变量:类型推导,实现高效编程
#include<iosteram>
using namespace std;
int main()
{
int num = 5;
const char *ptr = "hello world";
auto num1 = 5;//根据所赋值推导数据类型
auto ptr1 = "hello world";
int array[5] = {1,2,3,4,5} ;
for(auto temp : array)
{
cout << temp << endl;
}
vector<int> v = {1,2,3,4,5};
//vector<int>::iterate it;
for(auto it = v.begin(); it = v.end(); it++)
{
cout << *it << endl;
}
return 0;
}
auto ch1 = ‘a’;//
string s1 = “hello”;
string ::iterator it = s1.begin();
auto it2 = s1.begin();
关键字的升级typedef
C中typedef作用
typedef关键字的作用:给数据类型重命名
typedef工程应用作用
1、提高代码可读性
2、提高移植性
函数名命名规则
解决的是函数功能的可读性,无法解决形参的可读性
//提高代码的可读性
typedef int name_len;
int func(name_len a);//解释形参a的意思
typedef unsigned int Int;
Int num;
//想改int的类型
重命名:
pid_t pid;
size_t size;
mode_t mode;
off_t f;
typedef存在的问题
重命名函数指针时,可读性差
int add(int a,int b)
{
return a+b;
}
int main()
{
typedef int (*P_FUNC)(int,int);
//typedef int(*)(int,int) P_FUNC;
int (*p_add)(int,int);//p_add函数指针变量
P_FUNC p_add = add;//初始化
p_add(5,6);//调用
return 0;
}
C++中typedef的升级 typedef==using
using重命名
using关键的升级:替代C语言typedef的关键字
#include <iostream>
using namespace std;
int add(int a,int b)
{
return a+b;
}
int main()
{
using P_FUNC = int (*)(int,int);
using Int = int;
Int aa = 5;
P_FUNC p_add = add;
cout << p_add(5,6) << endl;
return 0;
}
using Int = int;//typedef int Int;
using Len = int;// typedef int Len;
using P_FUNC = int (*)(int,int);typedef
Int c;//int c;
Len c2;
add(6,5);
P_FUNC p_func;//int (*p_func)(int,int) = add;
p_func(6,5);
关键字const优化
C中const作用
将一个变量变为只读变量
什么是只读
const int num = 5;
int *p = & num;
*p = 7;
printf("num = %d\n",p);//还是会输出7,但是会报警告
int num = 5;
const int *p = & num;
int const *p = #//还是*p不可变
int * const p = #//是p不可变
*p = 7;
p++;//1
*p++;//2
num++;//3
//1、3可变
printf("num = %d\n",p);
工程应用的作用
修饰函数形参,保护实参在函数运行过程中不被改变
C++中const的升级
优化内容
const修饰的变量是常量
#include<iostream>
using namespace std;
int main()
{
int num = 5;
const int *p = #
cout << num << endl;
return 0;
}
constexpr
constexpr替换宏 #define
constexpr int getNum()
{
return 5;
}
#define MAX(a,b) a>b?a:b //傻瓜式替换 无法定义复杂的函数(不检查语法错误)
constexpr int cout = 5;//相当于宏定义,检查语法错误
//这里count就是常数5,不可变
cout++;//报错
C++对C的升级:类型的升级
引入bool类型
bool is_null = true;
bool is_null = flase;
三目运算符的升级
C语言中
int a = 5;
int b = 6;
(a>b?a:b) = 9;//报错,不可以这样赋值
C++语言中
1、结果可以作为左值
int a = 5;
int b = 6;
(a>b?a:b) = 9;//不报错,输出5,9
//while(a>b,a<b)支持,表达式
引用
C语言中
#include<stdio.h>
/*void swap(int a,int b)
{
int temp;
temp = a;
a=b;
b=temp;
}*/
void swap(int *a,int *b)
{
int temp;
temp = *a;
*a=*b;
*b=temp;
}
void func(char **ptr)
{
(*ptr)++;
]
char * func2()//不要返回局部变量的地址或者指针
{
static char src[100] = "heoll world";
return src;
}
int main()
{
int a =5;
int b = 6;
//swap( a,b);//实参变量名
swap(&a,&b);
//什么时候传地址,什么时候传值
//当修改实参变量值时,传实参变量的地址(传实参变量的地址,既可以使用也可以修改实参变量的值)
//当只使用不修改变量值时,传实参变量名(传实参变量名只能使用实参变量的值,不能修改)
printf("a = %d,b = %d\n",a,b);
char *ptr = "hello world";
func(ptr);//传的是实参变量名
func(&ptr);//实参变量地址
printf("ptr = %s\n",ptr);
char *result = func2();
printf("result = %s\n",result);//返回result = (null)
return 0;
}
C++语言中
引用作用
解决的是函数指针传参和返回值问题
语法作用
引用就是给变量起别名,操作引用相当于在操作引用所绑定的变量
#include<iostream>
using namespace std;
int main()
{
int count = 5;
int &r_count = count;
r_count++;
count++;
cout << count << endl;//两者相互影响,改变其中一个,另一个也改变
cout << r_count << endl;
}
注意事项
1、定义引用必须绑定变量(定义引用必须初始化,不能定义空引用)
2、一旦绑定就不能再次绑定到其他变量上
注:常引用:const修饰的引用,绑定常量
/*引用的作用:函数的形参和返回值*/
#include<iostream>
using namespace std;
void swap(int &a,int &b)//引用作为函数形参:传值和传地址问题
{
int temp = a;
a = b;
b = temp;
//实参和形参绑定
}
void test(const int &a)//a:常引用:只能使用不能修改其绑定的值
{
}
void & getNum(int a)//引用作为函数返回值
{
static int temp = a + 1;
return temp;
}
int main()
{
int a = 5;
int b = 6;
swap(a,b);
printf("a = %d b = %d\n",a,b);//两者交换
getNum(a) = 5;//函数调用做返回值:修改返回的值
//getNum(a);输出结果为7
cout << getNum(a) << endl;
}
作用场景
作为函数的形参,作为函数的返回值(函数的调用可以做左值)
int left = 5;
int right = 6;
swap(&left,*right);
count <<left << " "<< right;
void swap(int *left , int *right)
{
int temp;
temp = *left;
*left = *right;
*right = temp;
}
int temp = test();
test() = 10;//修改num的值
int left = 5;
int right = 6;
int &l_left = left;
cout << left << endl;
swap(&left,*right);
count <<left << " "<< right;
int const = 5;
int &
int func (const int &num1,const int num2)
{
}
void swap(int &left , int &right)
{
int temp;
temp = left;
*left = *right;
*right = temp;
}
int & test()
{
static int num = 5;
cout << num << endl;
return num;
}
引用是否占用额外的内存空间?
引用占用内存空间
编译器优化:不将这个空间展示给开发人员,不能操作这段空间“新的数据类型”
#include<iostream>
using namespace std;
struct node
{
int n;
int &rn;
};
int main()
{
cout << sizeof(struct node) << endl;//输出占用16字节
}
左值、右值引用
C++11:左值引用、右值引用
左值
可以被修改的值可以作为左值(可以取地址的可以作为左值)
左值引用
只能绑定左值 int &
右值
不可以被修改的值可以作为右值(不可以取地址的可以作为右值)
右值引用
只能绑定右值 int &&(对象移动)
eg:判断左值还是右值
int num = 5;//num是左值,5是右值
int *p = #//&num是左值还是右值
(&num)++;//报错,不能加加,所以&num是右值
eg:引用
void func1(int &l_num)//左值引用作形参
{
cout << l_num << endl;
}
void func2(int &&r_num)
{
cout << r_num << endl;
}
int main()
{
int num = 5;
int &l_num = num;
int &&r_num = 5;//移动构造函数,移动拷贝赋值运算符的重载
r_num++;//不报错,所有它是2左值
//int &&r_num1 = r_num;//报错
func1(num);
func1(l_num);//l_num++
//func1(5);//报错,单纯的左值引用不可以绑定常量
//上面函数改成 void func1(const int &l_num){}就可以
func2(5);
//func2(r_num);//报错,左对左,右对右,r_num是个左值
func2(std::move(r_num));//将左值转换成右值
func1(r_num);
return 0;
}
左值转换成右值
std::move();
eg
func2(std::move(r_num));//将左值转换成右值
作业
引用是不是数据类型?(不是)它占不占用内存空间(运行时不占用内存空间, 编辑时预留内存空间!!!!)
指针和引用的区别
什么是对象移动?对象移动的作用?
malloc/free升级,引入new、delete
malloc、free
#include<stdio.h>
#include<stdlib.h>
int main()
{
//void * malloc(size_t size);分配的空间大小
//void free(void *adrr);释放的空间地址
char *ptr = (char *)malloc(sizeof(char) * 100);//指针赋值一定是相同类型
int *p_arr = (int *)malloc(sizeof(char) * 100);//400字节(4*100)按字节分配
if(ptr == NULL)
{
perror("malloc error!");
exit(1);
}
memset(ptr,0,sizeof(char) * 100);//设初值
strcpy(ptr,"hello world!");//拷贝字符串
free(ptr);//free函数如何知道要释放多少内存空间?
ptr = NULL;
return 0;
}
new、delete
#include<iostream>
using namespace std;
int main()
{
/*分配的同时初始化*/
char *p = new char('a');//分配一个字节
cout << *p << endl;//输出a
//按类型个数分配
char *ptr = new char[100];
delete p;//释放
delete [] ptr;
// char *ptr = new char[100]{"hello world"};不支持
//int *p_arr = new int[10]{1,2,3,4};//支持
/*for(i=0;i<10;i++)
{
cout << p_arr[i] << endl;//没有输出
}*/
//分配二维数组
int (*p)[5]=new int[4][5];
char (*ptr)[100] = new char[3][100];
int **array2;//适合分配不规则的多维数组
array2 = new int *[2];
for(int i = 0;i<2;i++)
{
array2[i]=new int[3];
}
//分配三维数组
int (*p)[2][100] = new int[3][2][100];
return 0;
}
malloc free vs new delete
1、new、delete(暂时)理解为运算符 malloc、free是函数
2、new、delete按照类型的个数为单位 malloc、free按照字节为单位分配
3、new、delete:可以初始化 malloc、free只能手动初始化
(关于初始化:最好手动初始化 默认初始化都是0)
4、new、delete:如果分配失败会产生异常, malloc、free:返回NULL
5、new底层调用的是malloc delete底层调用还是free
6、new、delete:频繁分配小内存会导致内存产生碎片,同时会增加开销(系统) 池化技术:内存池
关于多维数组的分配
规则多维数组
int (*p)[3]=new int [3][3]
不规则的多维数组
int **array;
NULL升级
NULL: #define NULL (void *)0//0地址
C++中nullptr
char *ptr = nullptr;
函数的升级
C++对C的升级:函数的升级
引入inline关键字修饰函数:用(内存)空间换运行时间
引入默认参数:可以给形参设定默认参数 规则默认参数的右边参数必须全为默认参数
可以函数重载:可以使用相同的函数名 重载规则:形参的个数、类型或者顺序不同
注意事项:函数返回值不能作为重载条件,默认参数会影响重载规则
int add(int a,int b)
{
}
int add (int a,int b,int c)//个数不同
{
}
int add(char a, char b)
{
}
int add(int a,string b,int )//参数占位
{
}
add(6,5);
add(6,5);
add(6,5);
…
inline int add(int a, int b)//内联函数
{
return a+b;
}