一、C++简介
c++是一门以c为基础,发展而来的面向对象的程序设计语言。
组成:语言本身(基础语法)+ 库(标准库STL)
二、第一个C++程序
#include <iostream>
#include <stdio.h> //C++全面兼容C语法
int main()
{
printf("hello world!\n");
std::cout << "hello C++" << std::endl;
int a,b;
std::cin >> a >> b;
std::cout << a << "+" << b << "=" << a+b << std::endl;
return 0;
}
iostream:被包含的头文件名称。
cin : 表示标准输入 即stdin
cout :(console output) 表示标准输出 即stdout
cerr : (console input) 表示标准错误 即stderr
<< : 1.左移运算符;
2. 输出运算符(放到cout的右边,用于输出,把数据输出到标准输出)
在C++中,一切都是对象(拥有行为和属性),可以设置输出对象(cout)的属性
如:
设置输出的整数的进制
std::cout << std::hex; //设置输出对象的属性,每次输出数据都是16进制
>>: 1.右移运算符;
2.输入运算符(放到cin的右边,用于输入,用于从标准输入获取数据,存入到指定的变量)
cin >> 变量;
cin:console input 控制台输入,本身是一个全局变量
::
作用域运算符(std是“standard”,是标准c++库中的一个作用域,其中定义了标准库中的所有名字,其中专门用来存放名字的作用域称为名字空间namespace)。例如,std::cout指的是cout来自于std这个名字空间。
后缀名 .cpp
三、名字空间(namespace)
1.功能
用于存放各种名称,达到避免名字冲突。
2.本质
一块由程序员命名的内存区域。
3.定义方式
namespace 是定义名字空间的关键字
4.使用方法(先定义再使用)
1.使用作用域运算符(名字空间名称::要访问的名字)例如,std::cout
2.名字空间指令(using指令)例如,using namespace std; //把指定的名字空间中的 所有名字 导入到当前作用域,std::cout -------> cout
3.名字空间声明(using 声明)using 名字空间名称::名字;例如, using std::cout;//把指定的名字空间中的 指定名字 导入到当前作用域
例如:
#include <iostream>
using namespace std;
int a = 100;
namespace ns1
{
int a = 101;
namespace ns2
{
int a = 102;
};
};
void fun()
{
a = 1024;
}
int main()
{
int a = 103;
std::cout << "a=" << a << std::endl;
std::cout << "a=" << ns1::ns2::a << std::endl;
std::cout << "a=" << ::a << std::endl;
cout << "hello" <<endl;
return 0;
}
四、C++中的函数
1.默认参数(default argument)
须遵循的规则:
1.靠右原则
定义时:如果某一个参数设置了默认值,那么它右边所有的参数都必须有默认值
使用时:某一个参数使用了默认值,那么它后面的所有参数都必须使用默认值(不能跳着传参)
2.互斥原则
在函数的声明和定义分开的时候,只能把默认参数设置到声明的地方,定义的地方不能有默认参数
例如,设计一个函数,可以输出一个一维数组,数组元素默认以空格隔开,也可以指定的特殊的分隔符号
#include <iostream>
using namespace std;
void print_arr(int *arr,int numsSize,char ch = ' ')
{
for(int i = 0;i<numsSize;i++)
{
cout << a[i] << ch;
}
cout << "\b" << ' ';
cout << "\n";
}
int main()
{
int a[5] = {1,2,3,4,5};
print_arr(a,5,'#);
return 0;
}
2.函数重载(overload)
在C++中允许函数名重复使用,即使用同一个函数名可以定义多个不同的函数,实现相似的功能
例如
#include <iostream>
using namespace std;
int sum(int a,int b)
{
count << "sum_int _int" << endl;
return a+b;
}
double sum(double a,double b)
{
cout << "sum_double_double" << endl;
return a+b;
}
double sum(double a,int b)
{
cout << "sum_double_int" <<endl;
return a+b;
}
int main()
{
cout << sum(1,2) << endl;
cout << sum(1.1,2.2) << endl;
cout << sum(1.1,2) << endl;
return 0;
}
函数满足重载关系的基本条件:
1.在相同的作用域下面;
2. 函数名相同(不受返回值类型影响)
3.参数的类型列表不相同(个数,类型,是否有const属性)
存在的问题
例如: int sum(int a=0,int b=0,int c=0);
int sum(int a,int b);
sum(); //YES
sum(1,2,3); //YES
sum(1); //YES
sum(1,2); //二义性错误,不知道以sum(1,2);调用第二个,还是以sum(1,2,0);调用第一个
编译器换名机制
将函数名和参数类型结合在一起形成一个新的名字,替换掉原来的名字。
验证: g++ -c sum.cpp -o sum.o 编译为二进制文件(预处理/编译/汇编)
sum.o里面就有sum.cpp编译之后的所有符号信息
通过nm命令可以查看二进制文件的符号表信息
如: nm sum.o
0000000000000014 T _Z3sumdd
000000000000002e T _Z3sumdi
0000000000000000 T _Z3sumii
_Z3sumii _Z3sumdi _Z3sumdd 就是替换之后的函数名
3.混合编程(extern "C")
C++代码中调用了使用gcc编译的函数 。
//main.c
#include <stdio.h>
#include "nb.h"
int main()
{
int a[5] = {1,2,3,4,5};
printf_arr(a,5);
return 0;
}
//2main.cpp
#include <iostream>
#include "nb.h"
using namespace std;
int main()
{
cout << "in Cpp" << endl;
int a[5] = {1,2,3,4,5};
printf_arr(a,5);
return 0;
}
//./bin/nb.c
#include "nb.h"
#include <stdio.h>
void printf_arr(int *arr,int numsSize)
{
for(int i = 0;i<numsSize;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
//./bin/nb.h
#ifndef _NB_H_
#define _NB_H_
#ifdef _cplusplus
extern "C"
{
#endif
void printf_arr(int *arr,int numsSize);
#ifdef _cplusplus
}
#endif
#endif
nb.h/nb.c ------>编译成动态库
gcc -shared -fpic -o libnb.so nb.c
main.c ----->调用这个函数
gcc main.c -I./lib/ -lnb -L./lib
export LD_LIBRARY_PATH=./lib
2main.cpp ----->调用这个函数
g++ 2main.cpp -I./lib/ -lnb -L./lib
/tmp/ccI9ZyOR.o:在函数‘main’中:
2main.cpp:(.text+0x6a):对‘print_arr(int*, int)’未定义的引用
collect2: error: ld returned 1 exit status
解决方法:
在声明这个函数的时候,告诉编译器,这个函数的调用方法按照C语言的方式进行(不对函数名进行特殊处理)
4.内联函数(inline)
使用inline关键字修饰的函数,称为内联函数。
格式:
inline 返回值类型 函数名(形式参数类型列表)
{
函数体;
}
特点:
编译器在编译的时候,会将函数的函数体"复制"到函数的调用点
在调用函数的时候,就不需要跳转到函数定义的地方执行,可以节省函数调用的开销,提高程序运行效率
一般来说,内联函数都是简短.逻辑简单,不能包含复杂的运算(如:循环,递归)
一般使用内联函数来替换宏函数(宏定义)
//定义一个宏,返回两个数中小的那一个
#define MIN(a,b) ((a)<(b)?(a):(b)) //有bug
int a = 7,b = 8;
MIN(a++,b); //按照原来的意思,a只需要自增一次
//宏展开之后:
((a++)<(b)?(a++):(b)) //导致a多自增一次
//正确:
inline int MIN(int a,int b)
{
return a<b?a:b;
}
五、C++中的结构体/共用体/枚举
1.共用体(union)
C和C++中的共用体没有区别!!!
共用体和结构体有什么区别?
1、存储方式
结构体:结构体中的每个成员都有自己独立的存储空间,成员的存储顺序按照声明的顺序依次排列。
共用体:共用体的所有成员共享同一块存储空间,同一时刻只能存储其中一个成员的值。
2、内存占用
结构体:内存占用为其所有成员占用内存之和。
共用体:内存占用为其最大成员所占用的内存大小。
3、访问方式
结构体:可以同时访问结构体的所有成员。
共用体:在同一时刻只能访问其中一个成员。
2.枚举(enum)
枚举就是把一个变量所有可能出现的值,列举处理,变量的值,只能是枚举值中的一种
#include <iostream>
using namespace std;
enum class WEEK:char{MON,TUE,WED,THU,FRI,SAT,SUN};
enum class WORKDAY{MON,TUE,WED,THU,FRI};
int main()
{
WEEK w;
cout << sizeof(w) <<endl;
w = WEEK::MON;
int x = (int)WEEK::MON;
cout << "x="<< x << endl;
return 0;
}
3.结构体(struct)
在面试对象的编程语言中,一切都是对象 , C++中的结构体能够包含函数,用函数来表示这个对象的"行为"
任何对象都不仅仅只有静态的属性(数据)----成员变量,还有动态的行为(函数) ----成员函数
//舔狗代码
#include <iostream>
#include <string.h>
using namespace std;
struct Dog
{
int age;
char name[32];
void Jiao()
{
cout << "wangwangwang!" << endl;
}
void Tian()
{
cout << "宝,天气越来越热了,没有你的冷暴力真不知道怎么办了"<< endl;
cout << "my name is " << name << " i am " << age <<"years old!" << endl;
}
};
int main()
{
Dog w;
w.age = 21;
strcpy(w.name,"lisi");
w.Jiao();
w.Tian();
return 0;
}