C++学习笔记(1)

本文详细介绍了C++相对于C语言的扩展,包括类型增强(如bool和枚举)、输入输出(如iostream和格式控制)、函数重载、默认参数、引用、new/delete、内联函数、强制类型转换、命名空间以及系统string类的应用。重点讨论了这些特性的用法和优势,帮助读者深入理解C++的特性。
摘要由CSDN通过智能技术生成

C++相对于C的扩展:

一.类型增强

1. 类型检查更严格
左值右值的类型一致

2. 新增bool类型
C语言用0,1表示真假,在C++中使用bool类型。
bool类型相当于一个枚举

enum bool
{ false,true};

本质还是一样的

3. 真正的枚举
C语言中的枚举本质是整形,枚举变量可以用任意整形赋值,C++中只能使用在枚举体中初始化的元素

4. 表达式的值可被赋值
C语言中

int a,b = 5;
(a = b) = 10;   //仅C++可用
//printf(”a = %d,b = %d“,a,b);
//cout << a << b <<endl;

是不可使用的,即表达式不可作为左值
在C++中成立,表达式的值可以被赋值(注意赋值对象是表达式的左值a,即结果是a = 10 , b 依旧等于5)。

C++中:
尽量不使用指针
尽量不进行强制类型转换
尽量不要用字符数组,使用string类代替更安全

//char name[10];
//cin >>name;
string name;
cin >> name;

二.输入输出:

标准输入输出流:
cin cout

格式化输出:
cout默认输出6位有效数据

setw() 设置域宽
**setprecision()**设置有效位数
**setprecision(n)<<setiosflags(ios::fixed)**合用,可以设置小数点右边的位数
**setiosflags(ios::left)**设置左对齐
setfill(‘0’) 设置填充符

按进制输出:

int i = 123;
cout<<i<<endl;   //默认10进制输出
cout<<dec<<i<<endl;  //10进制输出
cout<<hex<<i<<endl;  //16进制输出
cout<<oct<<i<<endl;  //8进制输出
cout<<setbase(16)<<i<<endl;  //转为16进制输出

三.函数重载

C++支持同名函数

int func(int a)
{
}
int func(char a)
{
}

重名函数的要求:

  1. 参数个数,类型,顺序至少有一个不同则构成重载
  2. 返回类型不同不构成重载(意思是跟返回类型没关系)

重载函数在调用时的匹配原则:

  1. 根据参数类型,个数严格匹配,找到则调用
  2. 没有严格匹配的就依据隐式转换寻求匹配
    C++允许int到long,double;double到int,float的隐式类型转换

重载的底层实现原理:
name mangling技术(倾轧),对重名函数根据不同参数类型进行改名从而区分

extern"C"


倾轧发生在两个阶段,函数定义时(.cpp编译阶段)和函数声明使用时(.h声明阶段),只有两个阶段同时倾轧或者都不倾轧,才能匹配
extern “C” 可以避免函数倾轧
C++兼容C类库,.c文件的类库中函数没有倾轧,但是c++包含.h文件要被倾轧,就可以通过extern"C"来避免.h文件中的函数声明被倾轧。


这种说法比上面更好理解一点:
C++支持函数名重载,而C不支持,因此C++使用C库时,(用C++的规则编译C库)编译器生成目标文件时,函数名在目标文件中的临时内部名称规则不同。导致链接时符号对不上;而使用extern “C” 把声明的函数括起来,让其以C的规则编译,就对的上了。

参考


举例sting.h文件

extern "C" {
char * __cdecl _strset(char *_Str,int _Val) __MINGW_ATTRIB_DEPRECATED_S
EC_WARN;
char * __cdecl _strset_l(char *_Str,int _Val,_locale_t _Locale) __MINGW
_ATTRIB_DEPRECATED_SEC_WARN;
char * __cdecl strcpy(char * __restrict__ _Dest,const char * __restrict
__ _Source);

}

操作符重载:
C++提供了运算符重载机制。可以为自定义数据类型重载运算符。实现构造数据类型也可以像基本数据类型一样的运算特性

using namespace std;
struct COMP
{
float real;
float image;
};
COMP operator+(COMP one, COMP another)
{
one.real += another.real;
one.image += another.image;
return one;
}
int main()
{
COMP c1 = {1,2};
COMP c2 = {3,4};
COMP sum = c1+c2; //相当于调用函数operator+(c1,c2);
cout<<sum.real<<" "<<sum.image<<endl;
return 0;
}

重载了一个操作符+ ,可以实现结构体类型相加,其本质是函数的调用

四.默认参数:

C++支持函数定义时赋值(默认参数),调用时不传进参数就使用默认值

int function(int a = 1 ,int b = 2)
{
}
int main()
{
	function(); //a = 1;b = 2
    function(3);//a = 3,b = 2
}

默认参数的规则:

  1. 默认的顺序从右到左,不能跳跃(猜测因为传参是从左向右匹配的)
  2. 函数默认值和重载是冲突的,一个函数不能既作重载由有默认参数,编译器不能判断(判断重载是用参数作为依据的,如果使用默认参数就没法根据类型判断)
  3. 函数声明和定义一体时,默认参数在定义(声明)处,声明在定义前时,默认参数在声明处

五.引用:

引用是为已有的对象起个别名,对引用的所有操作都是在与之绑定的对象上进行的

int main()
{
int a;
int &b = a;
}

规则:

  1. 引用是对已有变量的声明,类型和地址与原变量一致,不分配内存
  2. 声明的时候必须初始化,一旦声明不可变更
  3. 引用时&符号作左值,作右值时作取地址符
  4. 可对引用再次引用,表示一个变量有多个别名
int a;
int &ra = a;
int &rra = ra; //表示a有两个别名ra 和rra

const:

  1. const对象的引用必须是const的,不能讲普通引用绑定到const对象
const int a = 1;
int &ra = a;//error
  1. const引用可以使 相关类型对象(常量,非同类型变量或表达式)初始化

常量:

const int &r = 2; //对常量的引用,合法

不同类型变量:

int a = 10
const double &rb = a; //合法

实际上相当于创建了临时变量,

double tmp = a;
const double &rb = tmp;

尽量使用const (能用就用) :

  • 使用const可以避免无意修改数据
  • const可以处理const 和非const 实参,否则只能接受非const实参
  • 使用const引用,可使实参和引用参数不匹配时能够正确生成并使用临时变量

C++中 const 定义的变量称为常变量。变量的形式,常量的作用,用作常量,常用于取代#define 宏常量。

链接:指针和引用的不同之处

六.new/delete

兼容malloc/free,new/delete是关键字而非函数

new用法:

int *p = new int ;//开辟大小为sizeof(int)的空间
int *a = new int(5);//开辟大小为sizeof(int)的空间,并初始化为5

int *b = new int[100];//开辟大小为100的整形数组空间
int **p = new *int [5];//开辟大小为5的整形指针数组空间

int (*a)[6] = new int[5][6];//开二维数组空间

delete用法:

int *a  = new int ;
delete a;//释放单个int空间

int *a = new int[5];
delete []a;//释放int数组空间

申请内存的返回值:
C中通过判断返回的指针是否为NULL来判断是否申请成功,
C++中内存申请失败会抛出异常,但一般不使用,还是自己判断

int *q = new (std::nothrow)int[10];
if(q == NULL)
return -1;

注意事项;

  1. new/delete比maloc/free效率更高
  2. 配对使用不能错
  3. new完一定不能忘记delete,避免内存泄露

七.内联函数inline

用法:

inline int func()
{
	return 0;
}

C及C++中的三种函数:
宏函数:
优:预处理时内嵌展开,避免函数调用开销
缺:易产生歧义;不会被编译器检查类型;被多次调用时多次内嵌易使text段体积增大

函数:
优:编译器会进行类型检查;不易产生歧义;不会多次内嵌,text段体积小;
缺:函数调用时压栈出栈,调用开销较大

内联函数:
综合上面两种优点,进行类型检查,又原地内嵌展开,避免调用开销
但只适合频繁调用,体积小的函数
inline只是对编译器的建议,如果函数行数较多还加inline,编译器会无视(register 修饰变量 类似)。

八.强制类型转换

静态类型转换:static_cast<目标类型>(标识符)
规则:可以隐式转换,则就可以静态转换

    int *p ;
    void *q;
    p = static_cast<int *>(q);
    q = static_cast<void *>(p);

强制转换可能会丢失精度,需要程序员自己判断。

重解释类型转换:reinterpret_case<目标类型>(标识符)

不可以隐式类型转换的,则需要重解释类型转换

脱常类型转换:const_cast<目标类型>(标识符)
目标类型只能是指针或引用
例1:

const int x = 200;
//int &a = x;//error,引用类型和变量类型不一致
int & a =const_cast<int&>(x);//脱掉了const

例2:

    const int d = 1;
    int *pd = const_cast<int *>(&d);
    *pd = 2;
    cout << "d = "<< d<<endl;
    cout << "*pd = "<< *pd <<endl;
    cout <<"&d = "<<&d<<endl;
    cout << "pd = "<< pd<<endl;

例2结果:

d = 1  //d没有被改变
*pd = 2  //
&d = 0x61fe7c  
pd = 0x61fe7c   //d地址和pd相同

例2说明const修饰的变量即使脱常了也不能通过指针被改变(好像宏一样)

使用 const_cast 去除 const 限定的目的不是为了修改它的内容,使用 const_cast 去除 const 限定,通常是为了函数能够接受这个实际参数。

动态类型转换:dynamic_cast<目标类型>(标识符)
用于多态中的父子类之间的强制转化
不是在编译阶段进行,是在运行时进行,运行时才知道转换结果是NULL还是有效对象。

C++中尽量使用这四种方式进行转换,而不要用C中的隐式和强制类型转换。

九.命名空间namespace

为了大型项目开发,而引入的一种避免命名冲突的一种机制,是对全局空间的再次划分

声明:

namespace SPACE
{
	全局变量 int a;
	数据类型 struct Stu{};
	函数 void func();
	其它命名空间 namespace
	
}

使用方法:

  1. 第一种:直接指定命名空间
SPACE::a = 5

这是标准用法,但是很麻烦

  1. 第二种:using + 命名空间 + 空间元素
using SPACE a;
a = 5;

用的少

  1. 第三种:using +namespace + 命名空间
using namespace SPACE;
a = 5;

如果不同命名空间内容不冲突,这种用法较多,出问题再用第一种

同名的命名空间自动合并,合作开发基于此
命名空间支持嵌套

匿名命名空间
namespace 后面没有名字

namespace 
{
	void func()
	{
	}
}

匿名命名空间的意义:

  • 作用类似于全局变量和函数前加static ,即该匿名命名空间内的变量或函数只能在本文件中使用(比static使用范围广)
  • 匿名命名空间的逻辑符合基本命名空间的一般原则
  • 多文件时使用,只有单文件时没必要使用

十.系统string类

C++使用string类处理字符串变量

定义及初始化

string str = "klyer";
cout<< str<<endl;

//string str;
//str = "klyer";

类型大小

string str = "klyer";
cout<<sizeof(str)<<endl;
cout<<sizeof(string)<<endl;

输出24??

常用运算

  • 赋值
string str1 = str2;//将str2值赋给str1

这里string不能少

  • 加法
string str3;
str3 = str1 +str2; //str2续在str1后面
cout<<str3<<endl;
  • 判断关系
string s1 = "abcdeg";
string s2 = "12345";
if(s1>s2)
cout<<"s1>s2"<<endl;
else
cout<<"s1<s2"<<endl;

成员操作函数

  • 下标操作
string str = "klyer";
str[3] = 'w';
cout<<str<<endl;  //输出klywr
  • 求字符串大小
    int size();
string str = "klyer";
cout<<str.size()<<endl; //输出为5
  • 返回C串
    为了C中的字符数组使用的
    char * c_str(); //返回char *型
string str = "klyer";
char buffer[10];
strcpy(buffer,str.c_str());
cout << buf << endl;
  • 查找
int find(char c, int pos = 0);//pos位开始查找 c,返回下标值,没有返回-1
  • 删除
    string &erase(int idx=0,int n = npos);
string str1 = "klyer";
string str2;//或者 string &str2 = str1;
str2= str1.erase(0,2); //从0开始删除往后数n位的字符串
cout <<str2<<endl;//输出 yer
  • 交换
    void swap(string &s2);
   string str1 = "klyer";
   string str2 = "1234";

    str1.swap(str2); //和交换str2
    cout<<str1<<endl;  //输出 1234
  • string 类型数组
string array[10]=
    {
        "0","12","234",
    };

使用二维数组存字符串,列数由最长字符串决定,浪费空间;
使用二级指针可以解决浪费内存问题,但是操作麻烦;
string类型数组存放字符串高效又灵活

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值