第一章 C++的初步知识
一、C++对C的扩充
1、格式化输入输出
(1)setw(x)——为其后面一个输出项预留x列的空间,如果输出数据项的长度不足x列,则数据向右对齐,若超过x列则按实际长度输出。需要头文件#include<iomanip>;
(2)setprecision(n)——设置精度
const double value = 12.3456789;
const int s=100;
cout << setprecision(4) << value << endl;
// 改成4精度,所以输出为12.35*
cout << fixed << setprecision(4) << value << endl;
// 加了fixed意味着是固定点方式显示,所以这里的精度指的是小数位,输出为12.3457
cout<<oct<<s<<endl;//以8进制输出,hex以16进制输出
2、用const定义常变量
const float pi=3.1415;
3、auto、decltype
(1)auto——类型说明符
auto x=5;//5是int类型,所以x是int类型
auto a;//错误,没有初始值无法确定类型
auto r=1,pi=3.14;//错误,类型混淆
(2)decltype类型指示符
const int c=0;
decltype(c) x=1;//x是const int 类型
4、动态内存分配
(1)用new申请的内存,必须用delete释放;用new[]申请的内存,必须用delete[]释放
(2)delete释放后,指针值不变,良好的风格是释放后指针置NULL,delete 指针之后 ,只是释放了指针指向的内存空间,而不是指针的内存空间
(3)申请一个对象
int *p1=new int;
delete p1;
p1=NULL;
(4)申请多个对象
int *p1=new int[10];
delete[] p1;
p1=NULL;
(5)申请一个类对象
class student{……};
student *p1=new student();
delete p1;
p1=NULL;
5、指针常量和常量指针
(1)指针常量
int a=2,b=3;
int *const p=&a;
p=&b;//错误
*p=b;//正确
(2)常量指针
int a=2,b=3;
const int *p;
p=&b;//正确
*p=b;//错误
(3)常指针常量
int a=2,b=3;
const int * const p=&a;
p=&b;//错误
*p=b;//错误
6、引用
(1)定义
引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。
用于在程序的不同的部分使用两个以上的变量名指向同一块地址,使得对其中任何一个变量的操作实际上都是对同一地址单元进行的。
(2)与指针的区别
引用定义了就一定要初始化,指针不用
从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域
引用初始化之后就不能再引用其他空间了,而指针的值在初始化之后可以改变
(3)举例
int a=3;
int &b=a;//b是对a的引用,此处&不表示取地址
b=4;//a的值也将变成4
(4)将引用作为函数参数
void swapyinyong(int &a,int &b){
int tmp;
tmp=a;
a=b;
b=tmp;
}//传递变量的别名
void swapzhizhen(int *a,int *b){
int tmp;
tmp=*a;
*a=*b;
*b=tmp;
}//传递变量的指针
int main(){
int i=3,j=4;
swapyinyong(i,j);
swapzhizhen(&i,&j);
}
(4)引用作为函数的实参有什么优缺点?
A、使用引用传参的话,在函数中对该变量所做的修改,在函数返回后依然存在
B、避免了变量复制的开销
7、命名空间
假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等。
同样的情况也出现在 C++ 应用程序中。例如,您可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个相同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。
因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
#include <iostream>
using namespace std;
// 第一个命名空间
namespace first_space{
void func(){
cout << "Inside first_space" << endl;
}
}
// 第二个命名空间
namespace second_space{
void func(){
cout << "Inside second_space" << endl;
}
}
int main (){
// 调用第一个命名空间中的函数
first_space::func();
// 调用第二个命名空间中的函数
second_space::func();
return 0;
}
8、条件编译
一般情况下,源程序中所有的行都参加编译。但有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是“条件编译”
便于程序移植或跨平台
#ifdef 标识符
程序段1
#else
程序段2
#endif
它的作用是当所指定的标识符已经被#define命令定义过,则在程序编译阶段只编译程序段1,否则只编译程序段2。#endif用来限定#ifdef命令的范围。其中,#else部分也可以没有。
题目:输入一个字母字符,使之条件编译,使之能根据需要将小写字母转化为大写字母输出,或将大写字母转化为小写字母输出。
#include<iostream>
using namespace std;
#define upper 1
int main(){
char a;
#if upper
cout<<"lowercase to uppercase"<<endl;
cout<<"please input a char:";
cin>>a;
if(a>='a'&&a<='z'){
cout<<a<<"===>"<<char(a-32)<<endl;
}else{
cout<<"data erroe"<<endl;
}
#else
cout<<"uppercase to lowercase"<<endl;
cout<<"please input a char:";
cin>>a;
if(a>='A'&&a<='Z'){
cout<<a<<"===>"<<char(a+32)<<endl;
}else{
cout<<"data erroe"<<endl;
}
#endif
cout<<"Good Night~"<<endl;
return 0;
}
二、函数概述
1、内联函数
(1)普通函数调用缺陷:时间开销大
(2)内联函数:在编译时将函数体代码插入到调用出;适用于代码短,频繁调用的场合
(3)定义:
inline 函数类型 函数名(参数表){
函数体;
}
(4)注意点:
不能出现递归
代码不宜过长
不宜出现循环
不能含有复杂控制语句如switch等
有些编译器会只能处理是否为内联函数
2、带默认形参值的函数
在函数定义或说明中为形参赋默认值
默认形参必须自最右向左连续定义
void f1(float a,int b=0,int c,char d='a');//错误
void f1(float a,int c,int b=0,char d='a');//正确
调用函数时,如省去某个实参,则该实参右边所有实参都要省略???
若函数声明时给出默认形参值,则函数定义时不能重复指定
作用:若调用给出实参值,则形参采用实参;若调用未给出实参值,则调用默认参数值;
3、函数重载
重载:同一符号或函数名对应多种操作(操作符/函数重载)
4、extern和static
(1)extern:
大型程序设计中所有模块共同使用的全局变量(函数)
在一个模块中定义全局变量,其他模块中用extern说明外来的全局变量
(2)static:
1)修饰全局变量时,表明一个全局变量只对定义在同一文件中的函数可见。
2)修饰局部变量时,表明该变量的值不会因为函数终止而丢失。
3)修饰函数时,表明该函数只在同一文件中调用。
4)修饰类的数据成员,表明对该类所有对象这个数据成员都只有一个实例。即该实例归 所有对象共有。
5)用static修饰不访问非静态数据成员的类成员函数。这意味着一个静态成员函数只能访问它的参数、类的静态数据成员和全局变量
三、C++程序的编写和实现
1、编写到运行
编辑——源程序.cpp——编译——目标程序.obj——连接——可执行目标程序.exe——运行