一、c++综述
(1)书籍推荐
①、c++ primer
②、c++ primer plus
③、c++编程思想
(2)c++历史
1982 年,美国 AT&T 公司贝尔实验室的 Bjarne Stroustrup 博士在 c 语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与 c 语言的渊源关系,它被命名为 C++。而 Bjarne Stroustrup(本贾尼·斯特劳斯特卢普)博士被尊称为C++语言之父。
各种语言排行榜可以到这个网站去找http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html
一本电子书
链接:https://pan.baidu.com/s/1PiV3Flzp5g4Gwdfcb60oBg
提取码:z9eq
复制这段内容后打开百度网盘手机App,操作更方便哦
二、c++相对于c的类型增强
(1)类型检查更严格
例如:在c语言中const int a=10;可以用指针对a进行更改,而在c++中不可以这样更改,类型更强了。所以说c++中的const是真正的const。不可以更改了。
(2)增加了bool类型、字符串类型(其实不是类型,是一个类,可以理解成类型)
string s;
bool b =true;//true代表1,可以直接用true和false
也可以赋值给b整数 如100等 c++中的bool其实就是一种bool类型
在C语言中使用enum和struct类型时,如在C语言中struct student{int a;int b;};在用的时候只能用struct student liming而在c++中就可以直接引用student liming来定义变量
当然 也可以用typedef进行类型转换,sizeof(bool)=1
(3)真正的枚举
c 语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而 c++中枚举变量,只能
用被枚举出来的元素初始化。
(4)部分表达式可以赋值
C语言中,表达式是不可以被赋值的, (a = b) = 100,这种是错的; 而C++中,表达式可以被赋值, (a = b) = 100,这是对的。先把b的值赋给a,再把100赋值给a;
三、c++的输入输出
cin cout 这是类对象
printf,scanf这是函数
一个非常简单的c++程序:
include <iostream>
using namespace std;
int main()
{
cout << "hello world "<< endl;
return 0;
}
fgets(name,30,stdin);//只能读29个
C语言中scanf("%d%c",&a&b);这样是不可以的,会把空格吃掉,最好是scanf("%d",&a);getchar();
用cin也是中间不能出现空格,不然空格后面的就会吃掉
设置输入输出格式
对于实型,cout 默认输出六位有效数据,setprecision(2) 可以设置有效位数,setprecision(n)<<setiosflags(ios::fixed)合用,可以设置小数点右边的位数。
include <iostream>
using namespace std;
int main()
{
int a=12345;
float b=4.8928972929;
cout<<setiosflags(ios::left)<<setw(8)<<a<<endl;//左对齐,域宽
cout<<setw(10)<<setprecision(2)<<setiosflags(ios::fixed)<<b<<endl;//小数点后两位
return 0;
}
输出十进制,十六进制,八进制。默认输出十进制的数据。
int i = 123;
cout<<i<<endl;
cout<<dec<<i<<endl;
cout<<hex<<i<<endl;
cout<<oct<<i<<endl;
cout<<setbase(16)<<i<<endl;
设置填充符
还可以设置域宽的同时,设置左右对齐及填充字符。
应该包含一个头文件,是一个<iomanip>
int main()
{
cout<<setw(10)<<1234<<endl;
cout<<setw(10)<<setfill('0')<<1234<<endl;
cout<<setw(10)<<setfill('0')<<setiosflags(ios::left)<<1234<<endl;
cout<<setw(10)<<setfill('-')<<setiosflags(ios::right)<<1234<<endl;
return 0;
}
四、函数重载(静多态)
重载就是一个东西两种用法,由语境决定(C语言没有,c++中才有) ambiguous(二义性)
①函数的名字相同,参数列表,个数,顺序,类型不同,但是返回值类型不构成重载条件
②匹配原则:严格匹配,不能严格匹配时进行隐式类型转换
重载原理:
重载底层实现(name mangling)
C++利用 name mangling(倾轧)技术,来改名函数名,区分参数不同的同名函数。
实现原理:用 v-c- i-f- l- d 表示 void char int float long double 及其引用。
void func(char a); // func_c(char a)
void func(char a, int b, double c); //func_cid(char a, int b, double c)
c++中倾轧就都倾轧,不倾轧就都不要倾轧
extern “C”(取消倾轧)
name mangling 发生在两个阶段,.cpp 编译阶段,和.h 的声明阶段。
只有两个阶段同时进行,才能匹配调用。
mystring.h
extern "C" {
int myStrlen(char *str);
}
mystring.cpp
int myStrlen(char *str)
//#include "mystring.h"
int myStrlen(char *str)
{
int len = 0;
while(*str++)
len++;
return len;
}
main.cpp
#include
#include "mystring.h"
using namespace std;
int main()
{
char *p = "china";
int len;
len = myStrlen(p);
return 0;
}
c++ 完全兼容 c 语言,那就面临着,完全兼容 c 的类库。由.c 文件的类库文件中函数名,
并没有发生 name mangling 行为,而我们在包含.c 文件所对应的.h 文件时,.h 文件要发生
name manling 行为,因而会发生在链接的时候的错误。
C++为了避免上述错误的发生,重载了关键字 extern。只需要,要避免 name manling
的函数前,加
extern "C" 如有多个,则 extern "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);
char * __cdecl strcat(char * __restrict__ _Dest,const char * __restrict
__ _Source);
int __cdecl strcmp(const char *_Str1,const char *_Str2);
size_t __cdecl strlen(const char *_Str);
size_t __cdecl strnlen(const char *_Str,size_t _MaxCount);
void *__cdecl memmove(void *_Dst,const void *_Src,size_t _Size) __MINGW
_ATTRIB_DEPRECATED_SEC_WARN;
}
五、操作符重载
c++认为一切操作符都是函数(有的操作符不这样认为……),函数是可以重载的,但是并不是所有的运算符都可以重载的
前面用到的<<本身在 c 语言中是位操作中的左移运算符。现在又用用流插入运算符,这
种一个字符多种用处的现像叫作重载。在 c 语中本身就用重载的现像,比如 & 既表示取地址,
又表示位操作中的与。*既表示解引用,又表示乘法运算符。
只不过 c 语言并没有开放重载机制。
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 = operator+(c1,c2); //c1+c2;
cout<
return 0;
}
示例中重载了一个全局的操作符+号用于实现将两个自定义结构体类型相加。本质是函数
的调用。
当然这个 COMP operator+(COMP one, COMP another),也可以定义为 COMP
add(COMP one, COMP another),但这样的话,就只能 COMP sum = add(c1,c2),而不
能实现 COMP sum = c1 +c2 了。
后序我们在学习完成类以后,重点讲解重载。
六、c++函数默认参数(默认参数(default parameters) )
通常情况下,函数在调用时,形参从实参那里取得值。对于多次调用用一函数同一实参
时,C++给出了更简单的处理办法。给形参以默认值,这样就不用从实参那里取值了。
当声明和定义不在一起时,默认参数应该写在声明处而不是定义处
单个参数
#include
#include
using namespace std;
void weatherForcast(char * w="sunny")
{
time_t t = time(0);
char tmp[64];
strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A ",localtime(&t) );
cout<
}
int main()
{
//sunny windy cloudy foggy rainy
weatherForcast();
weatherForcast("rainny");
weatherForcast();
return 0;
}
多个参数
float volume(float length, float weight = 4,float high = 5)
{
return length*weight*high;
}
int main()
{
float v = volume(10);
float v1 = volume(10,20);
float v2 = volume(10,20,30);
cout<<v<<endl;cout<<v1<<endl;cout<<v2<<endl;
return 0;
}
规则
1,默认的顺序,是从右向左,不能跳跃。
2,函数声明和定义一体时,默认参数在定义(声明)处。声明在前,定义在后,
默认参数在声明处。
3,一个函数,不能既作重载,又作默认参数的函数。当你少写一个参数时,系统无法
确认是重载还是默认参数。
void print(int a)
{ }
void print(int a,int b =10)
{ }
int main()
{
print(10);
return 0;
}
main.cpp:16: error: call of overloaded 'print(int)' is ambiguous
print(10);
七、引用(&)
引用的同时也要初始化
变量名,本身是一段内存的引用,即别名(alias)。此处引入的引用,是为己有变量起一个
别名。
声明如下:
int main()
{
int a;
int &b = a;
}
规则
1,引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型
与原类型保持一致,
且不分配内存。与被引用的变量有相同的地址。
2,声明的时候必须初始化,一经声明,不可变更。
3,可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。
4,&符号前有数据类型时,是引用。其它皆为取地址。
引用的本质是指针,C++对裸露的内存地址(指针)作了一次包装。又取得的指针的优良特性。所以再对引用取地址,建立引用的指针没有意义。
1,可以定义指针(int*也算是一种类型)的引用,但不能定义引用的引用。
int a;
int* p = &a;
int*& rp = p; // ok
int& r = a;
int&& rr = r; // error
案例:
#include <iostream>
using namespace std;
void swap(char *pa,char *pb)
{
char *t;
t = pa;
pa = pb;
pb = t;
}
void swap2(char **pa,char **pb)
{
char *t;
t = *pa;
*pa = *pb;
*pb = t;
}
void swap3(char * &pa,char *&pb)
{
char *t;
t = pa;
pa = pb;
pb = t;
}
int main()
{
char *pa = "china";
char *pb = "america";
cout<<"pa "<<pa<<endl;
cout<<"pb "<<pb<<endl;
// swap(pa,pb);
// swap2(&pa,&pb);
swap3(pa,pb);
cout<<"pa "<<pa<<endl;
cout<<"pb "<<pb<<endl;
return 0;
}
2,可以定义指针的指针(二级指针),但不能定义引用的指针。
int a;
int* p = &a;
int** pp = &p; // ok
int& r = a;
int&* pr = &r; // error
3,可以定义指针数组(数组 int 【3】也可以当成一种类型),但不能定义引用数组,可以定义数组引用。
int a, b, c;
int* parr[] = {&a, &b, &c}; // ok
int& rarr[] = {a, b, c}; // error
int arr[] = {1, 2, 3};
int (&rarr)[3] = arr; // ok
4,常引用
const 引用有较多使用。它可以防止对象的值被随意修改。因而具有一些特性。
(1)
const 对象的引用必须是 const 的,将普通引用绑定到 const 对象是不合法的。 这
个原因比较简单。既然对象是 const 的,表示不能被修改,引用当然也不能修改,必须使
用 const 引用。实际上,const int a=1; int &b=a;这种写法是不合法的,编译不过。
(2)
const 引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是
const 引用与普通引用最大的区别。const int &a=2;是合法的。double x=3.14; const int
&b=a;也是合法的。
常引用原理:
const 引用的目的是,禁止通过修改引用值来改变被引用的对象。const 引用的初始化
特性较为微妙,可通过如下代码说明:
double val = 3.14;
const int &ref = val; // int const & int & const ??
double & ref2 = val;
cout<<ref<<" "<<ref2<<endl;
val = 4.14;
cout<<ref<<" "<<ref2<<endl;
上述输出结果为 3 3.14 和 3 4.14。因为 ref 是 const 的,在初始化的过程中已经给
定值,不允许修改。而被引用的对象是 val,是非 const 的,所以 val 的修改并未影响 ref
的值,而 ref2 的值发生了相应的改变。
那么,为什么非 const 的引用不能使用相关类型初始化呢?实际上,const 引用使用相
关类型对象初始化时发生了如下过程:
int temp = val;
const int &ref = temp;
如果 ref 不是 const 的,那么改变 ref 值,修改的是 temp,而不是 val。期望对 ref 的
赋值会修改 val 的程序员会发现 val 实际并未修改。
int i=5;
const int & ref = i+5;
//此时产生了与表达式等值的无名的临时变量,
//此时的引用是对无名的临时变量的引用。故不能更改。
cout<<ref<<endl;
5,尽可能使用 const
use const whatever possible 原因如下:
1,使用 const 可以避免无意修改数据的编程错误。
2,使用 const 可以处理 const 和非 const 实参。否则将只能接受非 const 数据。
3,使用 const 引用,可使函数能够正确的生成并使用临时变量(如果实参与引用参
数不匹配,就会生成临时变量)
引用的本质浅析
1.大小与不可再引用
引用的本质是指针,是个什么样指针呢?可以通过两方面来探究,初始化方式和大小。
struct TypeP
{
char *p;
};
struct TypeC
{
char c;
};
struct TypeR
{
char& r; //把引用单列出来,不与具体的对像发生关系
};
int main()
{
// int a;
// int &ra = &a;
// const int rb; //const类型必须要初始化。
printf("%d %d %d\n",sizeof(TypeP),sizeof(TypeC),sizeof(TypeR));
return 0;
}
结论:
引用的本质是,是对常指针 type * const p 的再次包装。
char &rc == *pc double &rd == *pd
可以参考的文章:https://blog.csdn.net/luoweifu/article/details/39119487
https://blog.csdn.net/tianxiaolu1175/article/details/46889523
引用的数组和数组的引用 https://blog.csdn.net/kbccs/article/details/80114971
网友评论