- 该系列文章大部分摘自博主白鳯《C++面向对象程序设计》✍千处细节、万字总结(建议收藏)
http://t.csdn.cn/GxZ6U
- 如有不好的影响请联系删除
C++在非面向对象方面对C语言的扩充
输入和输出
int i;
float f;
cin >> i;
cout << f;
------------
scanf("%d", &i);
printf("%f", f);
----------------
连续读入
cin >> a >> b >> c;
cin
-
在默认情况下,运算符
>>
将跳过空白符,然后读入后面与变量类型相对应的值。因此,给一组变量输入值时可用空格符、回车符、制表符将输入的数据间隔开。 -
当输入字符串(即类型为string的变量)时,提取运算符
>>
的作用是跳过空白字符,读入后面的非空白字符,直到遇到另一个空白字符为止,并在串尾放一个字符串结束标志‘\0’。
C++允许在代码块中的任何地方声明局部变量。
const修饰符
在C语言中,习惯使用#define来定义常量,例如#define PI 3.14,C++提供了一种更灵活、更安全的方式来定义常量,即使用const修饰符来定义常量。例如const float PI = 3.14;
const可以与指针一起使用,它们的组合情况复杂,可归纳为3种:指向常量的指针、常指针和指向常量的常指针。
- 指向常量的指针:一个指向常量的指针变量。
const char* pc = "abcd";
//该方法不允许改变指针所指的变量,即
pc[3] = 'x'; //是错误的,
//但是,由于pc是一个指向常量的普通指针变量,不是常指针,因此可以改变pc所指的地址,例如
pc = "ervfs";
//该语句付给了指针另一个字符串的地址,改变了pc的值。
- 常指针:将指针变量所指的地址声明为常量
char* const pc = "abcd";
//创建一个常指针,一个不能移动的固定指针,可更改内容,如
pc[3] = 'x';
//但不能改变地址,如
pc = 'dsff'; //不合法
- 指向常量的常指针:这个指针所指的地址不能改变,它所指向的地址中的内容也不能改变。
const char* const pc = "abcd";
//内容和地址均不能改变
说明:
- 如果用const定义整型常量,关键字可以省略。即 const int bufsize = 100 与 const bufsize = 100等价;
- 常量一旦被建立,在程序的任何地方都不能再更改。
- 与#define不同,const定义的常量可以有自己的数据类型。
- 函数参数也可以用const说明,用于保证实参在该函数内不被改动。
void型指针
void通常表示无值,但将void作为指针的类型时,它却表示不确定的类型。这种void型指针是一种通用型指针,也就是说任何类型的指针值都可以赋给void类型的指针变量。
需要指出的是,这里说void型指针是通用指针,是指它可以接受任何类型的指针的赋值,但对已获值的void型指针,对它进行再处理,如输出或者传递指针值时,则必须再进行显式类型转换,否则会出错。
void* pc;
int i = 123;
char c = 'a';
pc = &i;
cout << pc << endl; //输出指针地址006FF730
//显式类型转换
cout << *(int*)pc << endl; //输出值123
pc = &c;
cout << *(char*)pc << endl; //输出值a
内联函数
在函数名前冠以关键字inline,该函数就被声明为内联函数。每当程序中出现对该函数的调用时,C++编译器使用函数体中的代码插入到调用该函数的语句之处,同时使用实参代替形参,以便在程序运行时不再进行函数调用。引入内联函数主要是为了消除调用函数时的系统开销,以提高运行速度。
注意
- 内联函数在第一次被调用之前必须进行完整的定义,否则编译器将无法知道应该插入什么代码
- 在内联函数体内一般不能含有复杂的控制语句,如for语句和switch语句等
- 使用内联函数是一种空间换时间的措施,若内联函数较长,较复杂且调用较为频繁时不建议使用
#include <iostream>
using namespace std;
inline double circle(double r) //内联函数
{
double PI = 3.14;
return PI * r * r;
}
int main()
{
for (int i = 1; i <= 3; i++)
cout << "r = " << i << " area = " << circle(i) << endl;
return 0;
}
使用内联函数替代宏定义,能消除宏定义的不安全性
带有默认参数值的函数
当进行函数调用时,编译器按从左到右的顺序将实参与形参结合,若未指定足够的实参,则编译器按顺序用函数原型中的默认值来补足所缺少的实参
void init(int x = 5, int y = 10);
init (100, 19); // 100 , 19
init(25); // 25, 10
init(); // 5, 10
- 在函数原型中,所有取默认值的参数都必须出现在不取默认值的参数的右边。
如 int fun(int a, int b, int c = 111);
- 在函数调用时,若某个参数省略,则其后的参数皆应省略而采取默认值。不允许某个参数省略后,再给其后的参数指定参数值。
函数重载
在C++中,用户可以重载函数。这意味着,在同一作用域内,只要函数参数的类型不同,或者参数的个数不同,或者二者兼而有之,两个或者两个以上的函数可以使用相同的函数名。
#include <iostream>
using namespace std;
int add(int x, int y)
{
return x + y;
}
double add(double x, double y)
{
return x + y;
}
int add(int x, int y, int z)
{
return x + y + z;
}
int main()
{
int a = 3, b = 5, c = 7;
double x = 10.334, y = 8.9003;
cout << add(a, b) << endl;
cout << add(x, y) << endl;
cout << add(a, b, c) << endl;
return 0;
}
注意
- 调用重载函数时,函数返回值类型不在参数匹配检查之列。因此,若两个函数的参数个数和类型都相同,而只有返回值类型不同,则不允许重载
//不允许
int mul(int x, int y);
double mul(int x, int y);
- 函数的重载与带默认值的函数一起使用时,有可能引起二义性。
void Drawcircle(int r = 0, int x = 0, int y = 0);
void Drawcircle(int r);
Drawcircle(20);
- 在调用函数时,如果给出的实参和形参类型不相符,C++的编译器会自动地做类型转换工作。如果转换成功,则程序继续执行,在这种情况下,有可能产生不可识别的错误。
void f_a(int x);
void f_a(long x);
f_a(20.83);
作用域标识符"::"
通常情况下,如果有两个同名变量,一个是全局的,另一个是局部的,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。
如果希望在局部变量的作用域内使用同名的全局变量,可以在该变量前加上“::”,此时::value代表全局变量value,“::”称为作用域标识符。
#include <iostream>
using namespace std;
int value; //定义全局变量value
int main()
{
int value; //定义局部变量value
value = 100;
::value = 1000;
cout << "local value : " << value << endl;
cout << "global value : " << ::value << endl;
return 0;
}
强制类型转换
可用强制类型转换将不同类型的数据进行转换。例如,要把一个整型数(int)转换为双精度型数(double),可使用如下的格式:
int i = 10;
double x = (double)i;
或
int i = 10;
double x = double(i);
以上两种方法C++都能接受,建议使用后一种方法。
new和delete运算符
程序运行时,计算机的内存被分为4个区:程序代码区、全局数据区、堆和栈。其中,堆可由用户分配和释放
C语言中使用函数malloc()和free()来进行动态内存管理。C++则提供了运算符new和delete来做同样的工作,而且后者比前者性能更优越,使用更灵活方便。
指针变量名 = new 类型
int *p;
p = new int;
delete 指针变量名
delete p;
下面对new和delete的使用再做一下几点说明
-
用运算符new分配的空间,使用结束后应该用也只能用delete显式地释放,否则这部分空间将不能回收而变成死空间。
-
在使用运算符new动态分配内存时,如果没有足够的内存满足分配要求,new将返回空指针
-
使用运算符new可以为数组动态分配内存空间,这时需要在类型后面加上数组大小\
指针变量名 = new 类型名[下标表达式];
int *p = new int[10];
- 释放动态分配的数组存储区时,可使用delete运算符。
delete []指针变量名;
delete p;
- new 可在为简单变量分配空间的同时,进行初始化
指针变量名 = new 类型名(初值);
int *p;
p = new int(99);
···
delete p;
- new 可在为简单变量分配空间的同时,进行初始化
指针变量名 = new 类型名(初值);
int *p;
p = new int(99);
···
delete p;
引用
引用(reference)是C++对C的一个重要扩充。变量的引用就是变量的别名,因此引用又称别名。
类型 &引用名 = 已定义的变量名
引用与其所代表的变量共享同一内存单元,系统并不为引用另外分配存储空间。实际上,编译系统使引用和其代表的变量具有相同的地址。
#include <iostream>
using namespace std;
int main()
{
int i = 10;
int &j = i;
cout << "i = " << i << " j = " << j << endl;
cout << "i的地址为 " << &i << endl;
cout << "j的地址为 " << &j << endl;
return 0;
}
上面代码输出i和j的值相同,地址也相同。
- 引用并不是一种独立的数据类型,它必须与某一种类型的变量相联系。在声明引用时,必须立即对它进行初始化,不能声明完成后再赋值。
- 为引用提供的初始值,可以是一个变量或者另一个引用。
- 指针是通过地址间接访问某个变量,而引用则是通过别名直接访问某个变量。
引用作为函数参数、使用引用返回函数值
#include <iostream>
using namespace std;
void swap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
int a[] = {1, 3, 5, 7, 9};
int& index(int i)
{
return a[i];
}
int main()
{
int a = 5, b = 10;
//交换数字a和b
swap(a, b);
cout << "a = " << a << " b = " << b << endl;
cout << index(2) << endl; //等价于输出元素a[2]的值
index(2) = 100; //等价于将a[2]的值赋为100;
cout << index(2) << endl;
return 0;
}
说明
- 不允许建立void类型的引用
- 不能建立引用的数组
- 不能建立引用的引用。不能建立指向引用的指针。引用本身不是一种数据类型,所以没有引用的引用,也没有引用的指针。
- 可以将引用的地址赋值给一个指针,此时指针指向的是原来的变量。
- 可以用const对引用加以限定,不允许改变该引用的值,但是它不阻止引用所代表的变量的值