您的位置:首页 > 编程语言 > C语言/C++

c++知识记录day01

2020-07-21 04:13 1076 查看

c++知识记录day01

  1. c++与c的不同点:
    *c++完全兼容c的所有内容
    *c++是面向对象的编程思想
    *c++支持运算符,函数重载,c++中运算符是可以作函数使用的
    *c++支持泛型编程,模板
    *c++支持异常处理
    *c++类型检查严格

  2. g++在ubuntu的安装命令为
    sudo apt-get update
    sudo apt-get install g++

  3. c++程序:
    *文件扩展名为xxx.cpp
    *头文件为include (其他头文件根据程序中使用的内容可加相关头文件)
    c中的include<stdio.h>可以继续使用,#include也可以使用
    *输入:cin >>
    输出 :cout <<
    cin与cout会自动识别类型,不需要占位符
    scanf与printf也可以继续使用
    注意:cout与cin是标准zz库类对象,printf与scanf是标准库函数
    *增加了名字空间,std::cout
    using namespace std;
    所有的标准类型,对象,函数都位于std命令空间中

  4. c++与c不同的数据类型
    *c++的结构:不再需要typedef,在定义结构变量的时候,可以省略struct关键字
    成员可以是函数(成员函数),在成员函数中可以直接访问成员变量,不要.或->,但是c的结构成员可以是函数指针
    有一些隐藏的成员函数(构造,析构,拷贝构造,赋值构造)
    可以继承,可以设置成员的访问权限,(面向对象)

#include <iostream>
using namespace std;
struct Student
{char name[20];
char sex;
int id;
};
int main()
{Student stu={"hehe",'m',20};
cout << stu.name << " " << stu.sex << " "<< stu.id << " " << endl;}

*c++的联合:不再需要 typedef ,在定义联合变量时,可以省略 union 关键字
成员可以是函数(成员函数),在成员函数中可以直接访问成员变量,不需要.或->
有一些隐藏的成员函数(构造、析构、拷贝构造、赋值构造)

#include<iostream>
using namespace std;
union Data
{int num;
char ch;
};
int main()
{Data d;
d.num=0x01020304;
cout << d.num << " " << (int)d.ch << endl;}

*c++的枚举:不再需要 typedef ,在定义枚举变量时,可以省略 enum 关键字
使用方法与C语言基本一致,但类型检查比C语言更严格

//c++中要比c的类型检查更为严格,如下的例子是枚举

#include<iostream>
using namespace std;
enum Key
{
KEY_UP=183,
KEY_DOWN=184,
KEY_RIGHT=185,
KEY_LEFT=186
};
int main()
{
Key k=KEY_UP;//c++中枚举赋值的时候只能赋值结构体中已经存在的,不能赋值别的数据,如给Key k=100,就会出现错误,但c语言中可以
cout << k <<endl;
}

c++的布尔类型:C++具有真的布尔类型,bool是C++中的关键字,在C语言中使用布尔类型需要导入头文件stdbool.h
在C++中 true false 是关键字,而在C语言中不是
在C++中 true false 是1字节,而在C语言中是4字节
c++的void:c语言中void 可以与任意类型的指针自动转换(万能指针)
c++中void不能给其他类型的指针直接赋值,必须强制类型转换,但其他类型的指针可以自动给void赋值
关于c++中为什么这样修改void*:是为了更安全,所以c++类型检查更为严格
c++可以自动识别类型,对万能指针的需求不再那么强烈
*c++的字符串:c++的字符串被封装成了string类,但可以与c中的字符串进行转换
需要string头文件,但已经包含在iostream中,被定义在std名字空间中
常见的字符串操作不再需要函数,可以使用运算符,如:=,==,!=,+=
计算长度使用它的成员函数size

  1. 名字空间:

*为什么需要名字空间
在项目中函数名、全局变量、结构、联合、枚举、类,非常有可能名字冲突,而名字空间就对这些命名进行逻辑空间划分(不是物理单元划分),为了解决命名冲突。
*什么是名字空间
在C++中经常使用使用多个独立开发的库来完成项目,由于库的作者或开发人员根本没有见过面,因此命名冲突再所难免,C++之父为防止命名冲突给C++设计一个名字空间的机制。
通过使用namespace XXX把库中的变量、函数、类型、结构等包含在名字空间中,形成自己的作用域,避免名字冲突。
namespace xxx
{

} // 没有分号
#include<iostream>
using namespace std;
namespace n1
{int num=100;}
namespace n2
{int num=200;}
namespace n3
{int num=300;}
int main()
{cout << n1::num << endl;
cout << n2::num << endl;
cout << n3::num << endl;}

*同名的名字空间有自动合并(为了声明和定义可以分开写)
同名的名字空间中如果有重名的依然会命名冲突。
名字空间的使用方法
::域限定符
空间名::标识符 // 使用麻烦,但是非常安全
using namespace 空间名; 把空间中定义的标识符导入到当前代码中
不建议这样使用,相当于把垃圾分类后又倒入同一个垃圾车,依然会冲突。

*无名名字空间
不属于任何名字空间的标识符,隶属于无名名字空间。
无名名字空间中的成员使用 ::标识符 进行访问。
如何访问被屏蔽的全局变量。

*名字空间嵌套
名字空间内部可以再定义名字空间,这种名字空间嵌套。
内层的名字空间与外层的名字空间的成员,可以重名,内层会屏幕外层的同名标识符。

namespace n1
{
int num = 1;
namespace n2
{
int num = 2;
namespace n3
{
int num = 3;
}
}
}
多层名字空间在使用时逐层分解。
n1::n2::n3::num;

*可以给名字空间取另名
由于名字空间可以嵌套,这样就会导致在使用内层成员时过于麻烦,可以给名字空间取别名来触决这类问题。
namespace n123 = n1::n2::n3;
6. c++的内存管理
*new/delete C++具备申请/释放堆内存功能的运算符。
相当于C语言中的malloc和free。
new 类型; 会自动计算类型所需要字节数,然后从堆分配对应字节数的内存,并返回内存的首地址(具备类型)。
delete 指针; 会自动释放堆内存。
注意:new/delete与malloc/free不能混用,因为new和delete会自动调用类、结构的构造函数、析构函数。
*数组的分配与释放
new 类型
; n表示数组长度,如果类、结构会自动调用n次构造函数。
delete[] 指针; 通过new[] 分配的内存,必须通过delete[]释放。
new[] 返回值前4个字节中存放在数组的长度。
注意:不能混用
*重复释放
delete/delete[]不能重复释放同一块内存。
delete/delete[]释放野指针的后果不确定,但释放空指针是安全的。
内存分配失败
当分配的内存过大,没有能需求的整块内存就会抛出异常std::bad_alloc。
new/delete与C语言的malloc/free相同点不同点(区别)?
身份:运算符 标准库函数
参数:类型(自动计算)字节数(手动计算)
返回值:带类型的地址 void地址
调用构造:自动调用 不能调用构造/析构函数
出错:抛异常 返回NULL

相同点:
1、都能管理堆内存
2、不能重复释放
3、可以释放NULL
#include<iostream>
#include<cstdlib>
using namespace std;
struct Student
{Student(void)
{cout << "我是构造函数" << endl;}
~Student(void)
{cout << "我是析构函数" << endl;}};
int main()
{Student* stu=(Student*)malloc(sizeof(Student));
Student* s1=new Student;}
  1. c++的函数:
    *函数重载:什么是函数重载?在同一作用域下,函数名相同,参数列表不同的函数,构成重载关系
    *重载实现的机制:c++代码在编译时会出现函数的参数类型添加到函数名中,借助这个方式实现函数重载,也就是c++的函数名在编译期间经历换名的过程
    因此,c++代码不能调用c函数(c语言编译器出的函数)
    注意:如果两个函数名真的一样,一定会冲突
    *extern “C” {}
    告诉C++编译器按照C语言的方式声明函数,这样C++就可以调用C编译器编译出的函数了(C++目标文件可以与C目标文件合并生成可执行程序)。
    如果C想调用C++编译出的函数,需要将C++函数的定义用 extern "C"包括一下。
    *重载和作用域
    函数的重载关系发生在同一作用域下,不同作用域下地同名函数,构成隐藏关系。
    *重载解析
    当调用函数时,编译器根据实参的类型和形参的匹配情况,选择一个确定的重载版本,这个过程叫重载解析。
    实参的类型和形参的匹配情况有三种:
    1、编译器找到与实参最佳的匹配函数,编译器将生成调用代码。
    2、编译器找不到匹配函数,编译器将给出错误信息。
    3、编译器找到多个匹配函数,但没有一个最佳的,这种错误叫二义性。
    在大多数情况下编译器都能立即找到一个最佳的调用版本,但如果没有,编译就会进行类型提升,这样备选函数中就可能具有多个可调用的版本,这样就可能产生二义性错误。
    *确定重载函数的三个步骤
    1、候选函数
    函数调用第一步就是确定所有可调用的函数的集合(函数名、作域),该集合中的函数就是候选函数。
    2、选择可行函数
    从候选函数中选择一个或多个函数,选择的标准是参数个数相同,而且通过类型提升实参可被隐式转换为形参。
    3、寻找最佳匹配
    优先每个参数都完全匹配的方案,其次参数完全匹配的个数,再其次是浪费内存的字节数。
    *指针类型也会对函数的重载造成影响
    C++函数的形参如果是指针类型,编译时函数名中会追加Px
    *默认形参
    1、在C++中函数的形参可以设置默认值,调用函数,如果没有提供实参数,则使用默认形参。
    2、如果形参只有一部分设置了默认形参,则必须靠右排列。
    3、函数的默认形参是在编译阶段确定的,因此只能使用常量、常量表达式、全局变量数据作为默认值。
    4、如果函数的声明和定义需要分开,只需要在函数声明时设置默认形参即可。
    5、默认形参会对函数重载造成影响,设置默认形参时一定要慎重。
    注意:重载的函数不要设置默认形参杂
    *内联函数
    1、普通函数调用时是生成调用指令(跳转),然后当代码执行到调用位置时跳转到函数所在的代码段中执行。
    2、内联函数就把函数编译好的二进制指令直接复制到函数的调用位置。
    3、内联函数的优点就是提高程序的运行速度(因为没有跳转,也不需要返回),但这样会导致可执行文件增大(冗余),也就是牺牲空间来换取时间。
    4、内联分为显式内联和隐式内联
    显式内联:在函数前 inline(C语言C99标准也支持)。
    隐式内联:结构、类中内部直接定义的成员函数,则该类型函数会被优化成内联函数。
    5、宏函数在调用时会把函数体直接替换到调用位置,与内联函数一样也是使用空间来换取时间,所以宏函数与内联的区别(优缺点)?
    1、宏函数不是真正的函数,只是代码替换,不会有参数压栈、出栈以及返回值,也不会检查参数类型,因此所有类型都可以使用,但这样会有安全隐患。
    2、内联函数是真正的函数,被调用时会进行传参,会进行压栈、出栈,可以有返回值,严格检查参数类型,但这样就不能通用,如果想补多种类型调用需要重载。
    6、内联适用的条件
    由于内联会造成可执行文件变大,并增加内存开销,因此只有频繁调用的简单函数适合作为内联函数。
    调用比较少的复杂函数,内联后并不显著提高性能,不足以抵消牺牲空间带来的损失,所以不适合内联。
    带有递归特性和动态绑定特性的函数,无法实施内联,因此编译器会忽略声明部分的inline关键字。

  2. 强制类型转换(显示类型转换)
    *.静态类型转换
    static_cast<目标类型>(源类型对象)
    两个对象只要有一个方向上能做隐式转换,那么两个方向上都可以做静态类型转换
    编译过程中会判断是否能进行静态类型转换,如果不能,报错
    *.动态类型转换
    dynamic_cast<目标类型>(源类型对象)
    源类型和目标类型必须是引用或者指针,而且源类型和目标类型存在着多态的继承关系,否则报错
    *.去常类型转换
    const_cast<目标类型>(源类型对象)
    源类型和目标类型必须是指针或者引用,而且源类型和目标类型只是在常属性上有区别,否则报错
    去除源类型对象的const属性
    *.重解释类型转换
    reinterpret_cast<目标类型>(源类型对象)
    源类型和目标类型必须是指针,或者一个是指针一个是整型,否则报错
    因为地址(指针)本质是一个整数,任何数据类型的地址都是4或8个字节,那么任何一个地址都可以解释为其它类型的地址

  3. 引用
    引用就是取艺名。
    *引用的基本特性
    引用就是取别名,声明一个标识符为引用,就表示该标识符是另一个对象的外号。
    引用必须初始化,不存在空引用,但有悬空引用(变量死了,名还留着)。
    可以引用无名对象和临时对象,但必须使用常引用。
    引用不能更换目标
    引用一旦完成了定义和初始化就和普通变量名一样了,它就代表了目标,一经引用终身不能再引用其它目标。
    引用目标如果具备const属性,那么引用也必须带cosnt属性。
    *引用型参数
    引用当作函数的参数能达到指针同样的效果,但不具备指针的危险,还比指针方便。
    引用可以在实现函数间共享变量的目的,而且是否使用引用由被调函数说了算。
    引用当作函数的参数还能提高传递参数效率,指针至少还需要4字节内存,而引用只需要增加一条标识符与内存之间的绑定(映射)。
    *引用型返回值
    不要返回局部变量的引用,会造成悬空引用。
    如果返回值是一个临时对象(右值),如果要使用引用接收的话,必须使用常引用。
    注意:C++中的引用是一种取别名的机制,而C语言中的指针是一种数据类型(代表内存编号的无符号整数)。
    注意:在C++中尽量使用 引用、new/delete。

  4. 操作符别名
    某些特殊语言的键没有~,&符号,所以C++标准委员会为了让C++更委竞争力,为符号定义了一此别名,让这些小语种也可以愉快编写C++代码。
    and &&
    or ||
    not !
    { <%
    } %>

#     :%`

常见的笔试面试题:(上面均有回答)
C语言中的结构与C++的结构有什么区别。
new/delete与C语言的malloc/free相同点不同点。
什么是函数重载。
内联函数与宏函数的相同点和不同点。
指针与引用的相当同点和不同。
以下三个本章小练习:
练习1:输入N个整数,计算出最大值是小值、平均值。

#include <iostream>
#include <climits>
using namespace std;
int  main()
{int n=0;
cout << "请输入N个整数: " ;
cin >> n;
int max=INT_MIN,min=INT_MAX,v=0;
float sum=0;
for(int i=0;i<n;i++)
{cin >> v;
if(v>max)
{max=v;}
if(v<min)
{min=v;}
sum+=v;
}
cout << max << " " << min << " " << sum/n << endl;
}
练习2:利用重载技术实现一个排序函数,要求所有基本类型都可以使用。
//利用重载技术实现一个排序函数,要求所有基本类型都可以使用

#include<iostream>
using namespace std;
void func(int num=100)
{cout << num <<endl;}
int main()
{func(1000);
func();}
练习3:实现一个C++版本的swap函数
//实现一个c++版的交换swap函数
#include<iostream>
using namespace std;
void swap(int& a,int& b)
{int t;
t=a;
a=b;
b=t;}
int main()
{int num1=3,num2=7;
cout << num1 << " " << num2 << endl;
swap(num1,num2);
cout << num1 << " " << num2 << endl;}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: