C++入门

C++入门

一. c++统一初始化方案
  1. 用花括号初始化一切,默认值为0;
int main(){
	int a{10};
    double b{};{0}
    int *c{a};
    int ar[]{1,2,3,4,5};
    int br[10]{};//{0 0 ...}
    int *e{};//null
    bool f{};//false;
}
  1. 花括号初始化不可以隐式转换;
double a=12.23;
int b=a;//得到a为12
//int c{a};//报错X
二. c++输入输出
  1. 引入头文件
  2. cin输入流
  3. cout输出流,"<<"插入符。
#include<iostream>//输入输出流头文件
using namesapce std;
int main(){
	char ch;
    int a;
    //cin,键盘
    cin >> ch >>a;
    //cout
    cout << "a = "<< a <<"ch="<<ch<<endl;
    return 0;
}
  1. cin字符串空格结束
#include<iostream>
int main(){
	char s[20];
	cin>>s;//
	//zhangxuan hello
	cout<<s;//zhangxuan
	char a[20];
	cin.getline(s,20,'#');
	//zhangxuan hello
	cout<<a;//zhangxuan hello
}
三.const和指针
  1. c语言const为变量,c++const为常量。

在c++中,编译阶段会将代码中的a全部变为常量5,而c语言认为其还是变量。

int main(){
	const int a=5;
	int s[a]={1,2,3,4,5};//c语言报错
	int b=0;
	int *p=(int *)&a;//强转
	*p=100;
	b=*p;//c语言为100,c++为5
	cout<<b<<" "<<*p;
	return 0;
}
  1. const和指针的使用。
int a=5;
int *b = &a;//可以更改指向和修改值
int const *c = &a;//只能修改指向
int * const d = &a;//只能修改值
int const * const e = &a;//都不能
  1. 指针指向时指针能力不能放大,否则不安全。
int a = 10;
const int *b=a;
//int *p1=b;//错误,b不能修改值,p1也不能修改值。
int const *p2=b;
//int * const p3=b;//错误,b不能修改值,p3也不能修改值
int const * const p4=b;
int a = 10;
int * const b=a;
//全部正确,因为b要求不能改指向,与其他是否改指向无关。
int *p1=b;
int const *p2=b;
int * const p3=b;
int const * const p4=b;
四. 引用(别名)
  1. 引用相当于别名,不能修改引用指向。
int a=5;
int &c=a;
cout<<a<<" "<<c;//5 5
c++;
cout<<a<<" "<<C;//6 6
cout<<&a<<" "<<&c;//相同地址
  1. 定义必须初始化,不能空引用
int a=5;
int &c;//错误,会报错,因为空引用
  1. 没有引用的引用
int a=5;
int &b=a;
int &&c=b;//错误,&&为右值引用
int &c=b;
  1. 函数传参数时使用
void Swap_Int(int *a,int *b){
	assert(a!=NULL&&b!=NULL)
	int *p=a;
	*a=*b;
	*b=*p;
}
//相当于别名,a和b为x和y的别名
void Swap_int(int&a,int&b){
    //没有空引用,无需判断
	int p=a;
	a=b;
	b=p;
}
int main(){
	int x=5,y=10;
	Swap_Int(&x,&y);
	Swap_int(x,y);
}
  1. const和引用

    • const修饰&,只能读不能改。
    int a=5;
    const int & b=a;
    b++;//错误,被const修饰,不能修改指向。
    int c=b;
    
    • const修饰引用,能读能改,系统会忽略const,因为引用本身不能改指向。
    int a=5;
    int & const b=a;
    b++;
    int c=b;
    
    • 引用过程中能力不能放大。
    const int a=5;
    int &b=a;//错误,不安全
    const int &c=a;
    int d=5;
    int &e=d;
    const int &f=d;
    const int &g=100;//底层为下述实现
    //tmp =100
    //const int &g=tmp
    
五. 指针和引用区别
  1. 语法规则上的区别

    • 指针变量存储的某个实例的地址,而引用是某个实例的别名。
    • 程序为指针变量分配内存区域,而不为引用分配内存区域。
    • 指针使用要加‘*’解引用,引用直接使用。
    • 指针变量的值可以改变,来存储其他实例的地址,引用在定义时被初始化,之后无法改变。
    • 指针可以为空(NULL),引用没有空引用。
    • 指针变量作为形参需要检测其合法性(判空NULL),引用不需要判空。
    • sizeof得到的指针变量的大小,而引用得到数据值的大小。
    • 指针级数没有限制,可以有指针的指针,引用不存在引用的引用,只有一级引用。
    • ++指针改变的指向,++引用改变的值。
  2. 本质上的区别

    • 在编译阶段,程序会将引用改为常性指针。
    int &a=b;
    int *const a=&b;//进行如下替换
    
六. inline内联函数

当函数开销小于开栈清栈开销使用inline,大于开栈清栈使用普通函数。

  1. 当代码行数小的时候,将代码直接放入调用处,不用建立栈空间,保护现场等操作。

  2. inline是一种以空间换时间的方法,省去了调用函数的开销,但当函数过长时或者是递归函数时即使加inline也不会展开。

  3. 定义和声明分开后会出错,所以内敛函数定义和声明不能分离。

  4. 使用需要设置配置信息:项目-属性-C/C++

    • 优化-内敛函数扩展-inline。
    • 常规-调试信息格式-数据程序库。
inline int add(a,b){
	return a+b;
}
int main(){
    int a=5,b=10;
    //函数在底层执行时会将函数展开
    //cout<<a+b<<endl;
    cout<<add(a,b)<<endl;
    return 0;
}
七. 缺省函数

在函数定义时给出缺省值,当函数给出实际值时改为实际值,如果没给出实际值使用缺省值,C语言不支持。

  • 缺省顺序必须从后往前
void func(int a,int b=5,int c=10,int d=15){
	cout<<a<<" "<<b<<" "<<c<<" "<<d;
}
void func(int a,int b=5,int c,int d)//错误
  • 函数的调用参数必须从左往右给
int main(){
	fun(1,2,3,4);//1,2,3,4
	fun(1,2,3);//1,2,3,15
	fun(1,2);//1,2,10,15
	fun(1,2,,4);//错误
}
  • 在多文件中,声明中给缺省值,定义不要给缺省值
//a.h
void func(int a,int b,int c=5,int d=10);
//a.cpp
#include "a.h"
void func(int a,int b,int c,int d){
	cout<<a<<" "<<b<<" "<<c<<" "<<d;
}
//main
#include "a.h"
int main(){
	func(1,2,3);
}
  • 形参可以为函数或者变量
int my_rand()
{
	srand(time(NULL));
	return rand()%10;
}
void func(int a,int b=my_rand()){
	cout<<a<<" "<<b<<endl;
}
八. 函数重载

C++可以函数同名,只要参数类型不同,或者类型相同但个数不同,就称为函数重载。

a. 七个无法重载的情况
  1. 函数名相同,参数表相同,返回类型不同,并不认为函数重载,即使名字粉碎后不同,但主函数无法区分。
double my_max(int a,int b){}
int my_max(int a,int b){}
my_max(5,10);//.并不能进行区分
  1. 参数表的比较过程于形参名无关。
void print(int *b,int n){}
void print(int *s,int len){}
print(&a,5);//并不能进行区分
  1. 参数表的比较过程与缺省实参无关。
int my_max(int a,int b=10){}
int my_max(int a,int b){}
my_max(5,10);//并不能进行区分
  1. 参数类型重定义但本质相同的不能算重载。
typedef unsigned int U_int;
void func(U_int a){}
void func(unsigned int b){}
func(10);//不能进行区分
  1. 如果形参是按值传递的,参数表比较时会忽略const和volatile。
void func(int a){}
void func(const int a){}
func(10);//无法区分
  1. 如果形参是按指针或引用传递的,参数表比较时const和volatile可以进行区分。
void func(int *p){}
void func(const int*p){}
void func(int &p){}
void func(const int&p){}
int *a;
const int *b;
int c=10;
func(a);
func(b);//可以区分
func(c);
func(10);
  1. 函数重载尽量不要使用缺省值,否则无法区分会报错。
void func(int a){}
void func(int a,int b=5){}
func(15);//无法区分
b. 重载与常性参数

普通指针或引用可以调用常性参数,但常性参数不能调用普通参数。

  1. 指针
void fun(int *p){}//a
void fun(const int *p){}//b
//void fun(int *const p){}//c
//a与c相同,无法正确编译,因为指针的传递本身就无法改变原指针的指向。
int a=5;
const int b=10;
fun(&a);//当a存在时调用a,当a不存在时调用b。
fun(&b);//当b存在时调用b,当b不存在时报错
  1. 引用
//void func(int p){}//a
void func(int &p){}//b
void func(int const &p){}//c
//编译时不会出错,但运行时出错,因为无法区分a和b。
int a=5;
const int b=10;
func(a);//当b存在时调用b,当b不存在时调用c。
func(b);//当c存在时调用c,否则报错
c. 重载的原因

名字粉碎(名字修饰):c或c++在函数内部通过修饰符给函数重命名为修饰名,然后通过重命名后的名称区分函数,修饰名是编译器在编译函数定义或者原型时生成的字符串。

  • __cdecl:默认修饰符。
  • __stdcall:标准修饰符。
  • __fastcall:快速修饰符。
  1. c语言
  • 默认修饰符在函数前加下划线。
  • 标准修饰符在函数前加下划线,在下划线后加@形参字节大小
void max(int a,int b){}
//默认修饰方法 粉碎为_max
void __stdcall max(double a,double b){}
//粉碎为_max@4
  1. c++语言
  • ?+函数名+@@YA+参数表+@Z。

  • 默认调用为@@YA,标准调用为@@YG

  • 参数表第一项为返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前。

  • 如果没有参数,以Z结尾而不是@Z。

  • //参数表信息
    X-void D-char E-unsigned char
    F-short H-int l-unsigened int
    j-long K-unsigned long
    M-float N-double _N-bool
    PA-指针,后面代码表明指针类型,如果同类型连续出现,以“0”代替,一个“0”代表一次重复。
    
//?my_max@@YAHHH@Z
int __cdecl my_max(int a,int b);
//?my_max@@YGDDD@Z
char __stdcall my_max(char a,char b);
//?my_max@@YANNN@Z
double my_max(double a,double b);

在c++环境下,用extern ”C“可将函数作为C方式重命名。反过来不可以,因为c++在设计上兼容c,c不兼容c++。

extern "C"
{
	void fun(int a){}
	void print(){}
}
九. 函数模板

函数模板可以创建一个通用功能的函数,以支持多种形参,简化重载函数的设计。

template<class T>
T my_max(T a,T b){
    cout<<typeid(T).name<<endl;
	return a>b?a:b;
}
int main(){
  int x=my_max(12,23);
  int y=my_max<int>(12,'a');//直接指定类型为int,不用推演。
  char ch=my_max('a','b');
  double dx=my_max(34.45,12.23);
}

函数模板是生成代码的代码,当遇见指定类型调用时,会进行推演然后生成指定类型的代码,再进行编译和链接,并不是简单的将T替换为指定类型。

//当遇见my_max(12,23)时,编译器生成下列代码
typedef int T;
T my_max<int>(T a,T b){
  cout<<typeid(T).name<<endl;//打印int
  return a>b?a:b;
}
//再进行编译链接
十. 名字空间

解决名字污染问题,防止出现名字冲突。

  1. 普通名字空间域
namespace zx{
	int g_max=10;
	int g_min=0;
	int my_add(int a,int t){
		return a+b;
	}
}
//相同名称的会合并
namespace zx{
    double pi=3.14;
}
  1. 命名空间套命名空间
namespace ldd{
    double pi=3.1415926;
double my_add(double a,double b)	{
        return a+b;
    }
    namespace Martrix{
        int c=10;
     char my_max(char a,char b){
            return a+b
     }
    }
}
  1. 多文件在.cpp内也要写命名空间。
//a.h
namespace zx{
	int add(int a,int b);
}
//a.cpp
#include"a.h"
namespace zx{
	int add(int a,int b){
		return a+b;
	}
}

//main
#include"a.h"
using namespace std;
int main(){
	cout<<add(5,10)<<endl;
}
  • 作用域解析符“::”(作用域限定符)
int	 main(){
	int a=zx::my_add(10,20);
    cout<<ldd::pi<<endl;
    cout<<ldd::Martrix::c<<endl;
}
  • using声明
  1. 声明作用域内的成员
using zx::pi;
using ldd::Martrix::my_max;
int main(){
	cout<<pi<<endl;
	cout<<my_max('a','b');
}
  1. 声明整个命名空间
using namespace zx;
int main(){
	cout<<pi<<endl;
    cout0<<add(5,10)<<endl;
}
十一.new/delete

空间申请区域有上越界标记和下越界标记,都是一个字节,fdfdfdfd填充。

  1. C语言的内存分配
    • malloc,申请空间。
    • realloc,扩充空间,c++没有填充。
    • calloc,申请空间,用0填充。
    • free释放空间。
int n=10;
int *ip1=(int *)malloc(sizeof(int));//用cdcd填充
int *ip2=(int*)calloc(n,sizeof(int));用0填充
ip2=(int*)realloc(ip2,sizeof(int)*n*2);//内存扩大到第二个参数。
free(ip1);
ip1=nullptr;
free(ip2);
ip2=nullptr;
  1. C++的内存分配

    • new开辟内存

      1. 申请空间

      2. 初始化

        • 初始化个数需要声明个数相同,否则初始化会占用下越界标记。
        int m=2;
        int *ip=new int[m]{1,2,3};
        //fdfdfdfd
        //01000000
        //02000000
        //03000000,本来应该是fdfdfdfd
        //其他
        
    • new的函数使用

    int *IP=(int *)::operator new(sizeof(int));//与malloc等价
    ::operator delete(ip1);//与free等价
    
    • 定位new的使用

    对已经申请的空间进行初始化。

    int n=10;
    int *ip1=(int *)malloc(sizeof(int));
    int *ip2=(int *)::operator new(siezof(int) *n);
    new(ip1)int{10};//定位new
    new(ip2)int[]{1,2,3,4,5,6};
    
    • delete
      • delete IP1,删除一个空间。
      • delete []IP2,删除一组空间。
int n=10;
int *ip1=new int(10);//申请一个初始化为10
int *ip2=new int[10];//申请十个int,初始化为cdcd
int *ip3=new int[10]{1,2,3,4,5,6};//申请十个int,初始化为1,2,3,4,5,6,其余为0
delete ip1;
delete []ip2;
delete []ip3;
  1. c和c++内存分配区别
    1. 对于内置类型没有区别。
    2. new/delete是运算符,malloc/free是函数。
    3. new自动计算大小,malloc手动计算大小。
    4. new可以初始化,malloc不能初始化。
    5. malloc返回值为void*,需要强转才能使用,new不需要。
    6. malloc申请失败返回的为NULL,new申请失败抛出异常。
十二. c++11的auto自动类型推导0
  1. auto定义的初始化必须给值,因为auto只是占位符。
auto x=10;//10>>X=int
auto dx=12.23;//12.23>>dx=double
auto fx=12.23f;//12.23f>>fx=float
auto ch='a';//'a'>>ch=char
const auto *xp=&a;//auto为int
auto ip=&x;//auto为int*
auto*sp=&x;//auto为int
//auto x=5,y;//报错,必须给初始值
//auto x=&x,y=6.0;//报错,出现二义性
  1. auto同指针和引用结合时,会考虑const限定符。
const int a=10;
auto b=a;//auto为int
auto *p=&a;//auto为const int
auto &c=a;//auto为const int
  1. 函数参数为auto,类似于函数模板。
void func(auto x){
	cout<<typeid(x).name()<<endl;
	cout<<x<<endl;
}
func(12);
func(12.23);
func('a');
  1. 无法推演数组的类型。
auto dr[10]={1,2,3,3,4,5};//报错
int ar[10]={1,2,3,4};
auto dr[10]=ar;//报错
int ar[10]={1,2,3,4};
auto &s=ar;//可以,甚至可以推出大小
  1. 可以推演函数返回值
template <class T>
T void(T a, T b){
	return a+b;
}
auto sum=void(5,10);
auto sum0=void(1.2,2.3);
十三. decltype关键字
  • 推演表达式的类型

    decltype(表达式),不计算表达式,只说出表达式的最终类型。

int x=10;
decltype(x) y=1;//int
decltype(x+y) z=10;//int
double dx=10;
decltype(x+dy) c;//double类型
const int a=5;
decltype(a)p;//const int
return 0;
十四. 基于范围的for循环
  • 只能遍历数组和容器
int ar[]={1,2,3};
int *ip=ar;
int (&ipa)[]=ar;
auto &ipd=ar;
for(auto:ipa){}//报错,不知道数组的大小
for(auto:ipd){}//正确,auto可以推出数组的大小
for(auto:ip){}//报错
//格式
for(ElemType val:array){
	//循环体
}
int main(){
	int ar[]={1,2,3,4,5,6,7,8,9};
	for(auto i:ar){
		cout<<i<<",";
    	//1,2,3,4,5,6,7,8,9
	}
    for(int i:ar){
        cout<<i<<",";
        i+=10;
        //1,2,3,4,5,6,7,8,9
        //因为每次将数组的元素赋值给i,所以对结果没有影响
    }
    for(int &i:ar){
        i+= 10;//以引用方式,可以改变值
    }
    for(const auto &x:ar){
        i+=10;//报错,const修饰
    }
}
十五. 指针空值–nullptr

由于c++对类型限制比较强,对于下述代码:

void *ip=0;
int *a=ip;

C语言可以编译通过。因为进行了隐式转换,C++不允许此处进行隐式转换,必须强制类型转换。

void *ip=0;
int *a=(int *)ip;

如果c++将NULL宏定义为((void *)0),则会出现下面情况:

int *a=(int*)NULL;
char *b=(char*)NULL;
double *c=(double*)NULL;

所以c++底层将NULL定义为0.

但如果出现下列代码

void fun(int a){};
void fun(char *p){};
fun(0);
fun(NULL);

很显然两个fun都调用第一个函数,与NULL调用参数为指针的fun函数不否,为了解决这个问题,c11引入nullptr指针类型空值常量,可以给一切指针赋值,但不能给其他类型赋值。

  1. c语言
#define NULL ((void*)0)
  1. c++
#define NULL 0 
char *ip=nullptr//指针空值类型常量,指针零值,可以向一切指针赋值
sizeof(nullptr)==sizeof((void*)0);//相同
int a=nullptr;//错误,类型不同,指针空值类型和int类型。

nullptr可以给任何指针赋值,表示指针空值类型。

十六. typedef和using
  1. typedef和using都可以重命名
typedef unsigned int u_int32;
using   u_int32 = unsigned int;
  1. using可以和模板结合使用
template<class T>
using pointer=T*;
int main(){
	int a=5;
	pointer<int>p=&a;
	char ch='a';
	pointer<char>cp=&ch;
}
  1. using可以声明名字空间
using namespace std;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值