1.C++的初识
1.1 简单入门
cpp
#include<iostream>
using namespace std;
int main() {
cout << "hello world" << endl;
return 0;
}
- #include<iostream>; 预编译指令,引入头文件iostream.
- using namespace std; 使用标准命名空间
- cout << "hello world"<< endl; 和printf功能一样,输出字符串"hello wrold"
问题1:c++头文件为什么没有.h?
在c语言中头文件使用扩展名.h,将其作为一种通过名称标识文件类型的简单方式。但是c++得用法改变了,c++头文件没有扩展名。但是有些c语言的头文件被转换为c++的头文件,这些文件被重新命名,丢掉了扩展名.h(使之成为c++风格头文件),并在文件名称前面加上前缀c(表明来自c语言)。例如c++版本的math.h为cmath.
问题2:using namespace std 是什么?
namespace是指标识符的各种可见范围。命名空间用关键字namespace 来定义。命名空间是C++的一种机制,用来把单个标识符下的大量有逻辑联系的程序实体组合到一起。此标识符作为此组群的名字。
问题3:cout 、endl 是什么?
cout是c++中的标准输出流,endl是输出换行并刷新缓冲区。
1.2 namespace 【命名空间】
C++中自定义命名空间(namespace)及其使用的三种方法_自定义namespace-CSDN博客
由于namespace的概念,使用C++标准程序库的任何标识符时,可 以有三种选择:
1)直接指定标识符。例如std::ostream而不是ostream。完整语句如 下:
cpp
std::cout << std::hex << 3.4 << std::endl;
2)使用using关键字。
cpp
using std::cout;
using std::endl;
using std::cin;
3)最方便的就是使用using namespace std;
4.命名空间使用语法
- 创建一个命名空间:
cpp
namespace A{
int a = 10;
}
namespace B{
int a = 20;
}
void test(){
cout << "A::a : " << A::a << endl;
cout << "B::a : " << B::a << endl;
}
2.命名空间只能全局范围内定义(以下错误写法)
cpp
void test(){
namespace A{
int a = 10;
}
namespace B{
int a = 20;
}
cout << "A::a : " << A::a << endl;
cout << "B::a : " << B::a << endl;
}
3.命名空间可以嵌套命名
cpp
namespace A{
int a = 10;
namespace B{
int a = 20;
}
}
void test(){
cout << "A::a : " << A::a << endl;
cout << "A::B::a : " << A::B::a << endl;
}
4.命名空间是开放的,可以随时加入新的成员
cpp
namespace A{
int a = 10;
}
namespace A{
void func(){
cout << "hello namespace!" << endl;
}
}
5.声明和实现可以分离
cpp
#pragma once
namespace MySpace{
void func1();
void func2(int param);
}
void MySpace::func1(){
cout << "MySpace::func1" << endl;
}
void MySpace::func2(int param){
cout << "MySpace::func2 : " << param << endl;
}
6.命名空间起别名
cpp
namespace veryLongName{
int a = 10;
void func(){ cout << "hello namespace" << endl; }
}
void test(){
namespace shortName = veryLongName;
cout << "veryLongName::a : " << shortName::a << endl;
veryLongName::func();
shortName::func();
}
2.C++对C的扩展
2.1 ::作用域运算符
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
//定义一个命名空间
namespace spaccA {
int g_a = 10;
}
namespace spaceB {
int a = 20;
namespace spaceC {
struct teacher
{
int id;
char name[64];
};
}
namespace spaceD {
struct teacher
{
int id;
char name[64];
};
}
using namespace spaceC;
}
int main(void)
{
//using spaccA::g_a;
using namespace spaccA;
int a = 10;
cout << g_a << endl;
//spaceB::spaceC::teacher t1;
//using spaceB::spaceC::teacher;
//teacher t1;
//using namespace spaceB::spaceC;
//spaceB::spaceC::teacher t1;
using namespace spaceB;
teacher t1;
t1.id = 10;
//spaceB::spaceD::teacher t2;
//t2.id = 20;
return 0;
}
2.2 "实用性"增强
c语言一定要在使用之前先定义
c++可以在使用的时候进行定义
cpp
//C语⾔中的变量都必须在作⽤域开始的位置定义!!
//C++中更强调语⾔的"实⽤性",所有的变量都可以在需要使⽤时再定义。
#include<iostream>
using namespace std;
int main() {
int i = 0;
cout << "i=" << i << endl;
int k = 4;
cout << "k=" << k << endl;
return 0;
}
2.3 变量检测增强
c语言允许定义多个同名的变量
c++不能定义多个同名变量,即使作用域不同
cpp
/*
在C语⾔中,重复定义多个同名的全局变量是合法的
在C++中,不允许定义多个同名的全局变量
C语⾔中多个同名的全局变量最终会被链接到全局数据区的同⼀个地址空间上
int g_var;
int g_var = 1;
C++直接拒绝这种⼆义性的做法。
*/
#include<iostream>
int g_var;
int g_var = 1;
int main(int argc, char* argv[])
{
printf("g_var = %d\n", g_var);
return 0;
}
2.4 C++中所有变量和函数都必须有类型
C++中所有的变量和函数都必须有类型
C语⾔中的默认类型在C++中是不合法的
函数f的返回值是什么类型,参数⼜是什么类型?
函数g可以接受多少个参数?
cpp
//i没有写类型,可以是任意类型
int fun1(i){
printf("%d\n", i);
return 0;
}
//i没有写类型,可以是任意类型
int fun2(i){
printf("%s\n", i);
return 0;
}
//没有写参数,代表可以传任何类型的实参
int fun3(){
printf("fun33333333333333333\n");
return 0;
}
//C语言,如果函数没有参数,建议写void,代表没有参数
int fun4(void){
printf("fun4444444444444\n");
return 0;
}
g(){
return 10;
}
int main(){
fun1(10);
fun2("abc");
fun3(1, 2, "abc");
printf("g = %d\n", g());
return 0;
}
|----------------------------------------------------------------------------------------------------------------------------------|
| 1. 在C语言中,int fun() 表示返回值为int,接受任意参数的函数,int fun(void) 表示返回值为int的无参函数。 2. 在C++ 中,int fun() 和int fun(void) 具有相同的意义,都表示返回值为int的无参函数。 |
2.5 更严格的类型转换
在C++,不同类型的变量一般是不能直接赋值的,需要相应的强转。
以上c代码c编译器编译可通过,c++编译器无法编译通过。
cpp
typedef enum COLOR{ GREEN, RED, YELLOW } color;
int main(){
color mycolor = GREEN;
mycolor = 10;
printf("mycolor:%d\n", mycolor);
char* p = malloc(10);
return EXIT_SUCCESS;
}
2.6 struct类型加强
- c中定义结构体变量需要加上struct关键字,c++不需要。
- c中的结构体只能定义成员变量,不能定义成员函数。c++即可以定义成员变量,也可以定义成员函数。
cpp
//1. 结构体中即可以定义成员变量,也可以定义成员函数
struct Student{
string mName;
int mAge;
void setName(string name){ mName = name; }
void setAge(int age){ mAge = age; }
void showStudent(){
cout << "Name:" << mName << " Age:" << mAge << endl;
}
};
//2. c++中定义结构体变量不需要加struct关键字
void test01(){
Student student;
student.setName("John");
student.setAge(20);
student.showStudent();
}
2.7 对函数返回参数个数的要求
C传递参数大于实际定义的参数,只会报警告,C++传递参数大于实际定义的参数会报错
2.8 新增的bool类的关键字
bool b=100;//无论将修改为多少,sizeof(b)永远为1
cpp
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int a;
bool b = true;
printf("b = %d, sizeof(b) = %d\n", b, sizeof(b));//1 1
b = 4;
a = b;
printf("a = %d, b = %d\n", a, b);//1 1
b = -4;
a = b;
printf("a = %d, b = %d\n", a, b);//1 1
a = 10;
b = a;
printf("a = %d, b = %d\n", a, b);//10 1
b = 0;
printf("b = %d\n", b);//0
return 0;
}
2.9 三目运算符功能增强
- c语言三目运算表达式返回值为数据值,为右值,不能赋值。
cpp
int a = 10;
int b = 20;
printf("ret:%d\n", a > b ? a : b);
//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?
//(a > b ? a : b) = 100;//错误
//返回的是右值
- c++语言三目运算表达式返回值为变量本身(引用),为左值,可以赋值。
cpp
int a = 10;
int b = 20;
printf("ret:%d\n", a > b ? a : b);
//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?
cout << "b:" << b << endl;//20
//返回的是左值,变量的引用
(a > b ? a : b) = 100;//返回的是左值,变量的引用
cout << "b:" << b << endl;//100
2.10 const增强
const单词字面意思为常数,不变的。它是c/c++中的一个关键字,是一个限定符,它用来限定一个变量不允许改变,它将一个对象转换成一个常量。
1.c语言中的const
们c中的const理解为"一个不能改变的普通变量",也就是认为const应该是一个只读变量,既然是变量那么就会给const分配内存 ,并且在c中const是一个全局只读变量,c语言中const修饰的只读变量是外部连接的。
2.c++中的const
**在c++中,一个const不必创建内存空间,而在c中,一个const总是需要一块内存空间。**在c++中,是否为const常量分配内存空间依赖于如何使用。一般说来,如果一个const仅仅用来把一个名字用一个值代替(就像使用#define一样),那么该存储局空间就不必创建。
3.C/C++中const异同总结
cpp
//const 定义常量---> const 意味只读
const int a;
int const b;
//第⼀个第⼆个意思⼀样 代表⼀个常整形数
const int *c;
//第三个 c是⼀个指向常整形数的指针(所指向的内存数据不能被修改,但是本⾝可以修改)
int * const d;
//第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
const int * const e ;
//第五个 e⼀个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)
4.const 和 #define 的相同
cpp
#include <iostream>
//#define N 10
int main()
{
const int a = 1;
const int b = 2;
int array[a + b] = {0};
int i = 0;
for(i = 0; i < (a+b); i++)
{
printf("array[%d] = %d\n", i, array[i]);
}
return 0;
}
C++中的const修饰的,是一个真正的常量,而不是C中变量(只读)。在 const修饰的常量编译期间,就已经确定下来了
5.const 和 #define 的区别
cpp
#include <iostream>
void fun1()
{
#define a 10
const int b = 20;
}
void fun2()
{
printf("a = %d\n", a);
//printf("b = %d\n", b);
}
int main()
{
fun1();
fun2();
return 0;
}
- const有类型,可进行编译器类型安全检查。#define无类型,不可进行类型检查.
- const有作用域,而#define不重视作用域,默认定义处到文件结尾.如果定义在指定作用域下有效的常量,那么#define就不能用。
辨析;
cpp
#include <stdio.h>
int main()
{
const int a = 10;
//将a的地址传递给p
int* p = (int*)&a;
printf("a===>%d\n", a);//10
*p = 11;
printf("a===>%d\n", a);//10
return 0;
}
cpp
#include <iostream>
//#define N 10
int main()
{
const int a = 1;
const int b = 2;
int array[a + b] = { 0 };
int i = 0;
for (i = 0; i < (a + b); i++)
{
printf("array[%d] = %d\n", i, array[i]);//0 0 0
}
return 0;
}
cpp
#include <iostream>
void fun1()
{
#define a 10
const int b = 20;
}
void fun2()
{
printf("a = %d\n", a);//10
//printf("b = %d\n", b);
}
int main()
{
fun1();
fun2();
return 0;
}
2.11 真正的枚举
c 语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而 c++中枚举 变量, 只能用被枚举出来的元素初始化。
cpp
#include<iostream>
using namespace std;
enum season {SPR,SUM,AUT,WIN};
int main()
{
enum season s = SPR;
//s = 0; // error, 但是C语⾔可以通过
s = SUM;
cout << "s = " << s <<endl;//1
return 0;
}
3 引⽤(reference)
3.1 引用基本用法
**引用是c++对c的重要扩充。**在c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递(pass-by-reference),它也存在于其他一些编程语言中,并不是c++的发明。
- 变量名实质上是一段连续内存空间的别名,是一个标号(门牌号)
- 程序中通过变量来申请并命名内存空间
- 通过变量的名字可以使用存储空间
|--------------------------------------------------------|
| 对一段连续的内存空间只能取一个别名吗? c++中新增了引用的概念,引用可以作为一个已定义变量的别名。 |
基本语法
cpp
Type & ref=val;
int & ref=&a;//表示ref是a的别名
cpp
#include<iostream>
using namespace std;
int main() {
int a = 10;
int& b = a;//int* b
a = 11;
{
int* p = &a;
*p = 12;
cout << a << endl;//12
}
b = 14;
cout << a <<"" << b << endl;//14 14
}
注意事项:
- &在此不是求地址运算,而是起标识作用。
- 类型标识符是指目标变量的类型
- 必须在声明引用变量时进行初始化。
- 引用初始化之后不能改变。
- 不能有NULL引用。必须确保引用是和一块合法的存储单元关联。
- 可以建立对数组的引用。
cpp
//1. 认识引用
void test01(){
int a = 10;
//给变量a取一个别名b
int& b = a;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "------------" << endl;
//操作b就相当于操作a本身
b = 100;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "------------" << endl;
//一个变量可以有n个别名
int& c = a;//给c取了一个名为"c"的别名
c = 200;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "c:" << c << endl;
cout << "------------" << endl;
//a,b,c的地址都是相同的
cout << "a:" << &a << endl;
cout << "b:" << &b << endl;
cout << "c:" << &c << endl;
}
//2. 使用引用注意事项
void test02(){
//1) 引用必须初始化
//int& ref; //报错:必须初始化引用
//2) 引用一旦初始化,不能改变引用
int a = 10;
int b = 20;
int& ref = a;
ref = b; //不能改变引用
//3) 不能对数组建立引用
int arr[10];
//int& ref3[10] = arr;
}
3.2 注意点
1.一个变量可以有n个别名
2.操作别名等于操作原数
3.别名和操作数的地址是一样的
4.引用必须初始化
5.一旦初始化,不能进行修改
6.不能对数组建立引用
3.3 建立对数组的引用
方法一:
cpp
typedef int ArrRef[10];
int arr[10];
ArrRef& aRef = arr;
for (int i = 0; i < 10;i ++){
aRef[i] = i+1;
}
for (int i = 0; i < 10;i++){
cout << arr[i] << " ";
}
cout << endl;
方法二:
cpp
int(&f)[10] = arr;
for (int i = 0; i < 10; i++){
f[i] = i+10;
}
for (int i = 0; i < 10; i++){
cout << arr[i] << " ";
}
cout << endl;
3.4 函数中的引用
当引用被用作函数参数的时,在函数内对任何引用的修改,将对还函数外的参数产生改变。
cpp
//值传递
void ValueSwap(int m,int n){
int temp = m;
m = n;
n = temp;
}
//地址传递
void PointerSwap(int* m,int* n){
int temp = *m;
*m = *n;
*n = temp;
}
//引用传递
void ReferenceSwap(int& m,int& n){
int temp = m;
m = n;
n = temp;
}
void test(){
int a = 10;
int b = 20;
//值传递
ValueSwap(a, b);
cout << "a:" << a << " b:" << b << endl;
//地址传递
PointerSwap(&a, &b);
cout << "a:" << a << " b:" << b << endl;
//引用传递
ReferenceSwap(a, b);
cout << "a:" << a << " b:" << b << endl;
}
通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单:
- 函数调用时传递的实参不必加"&"符
- 在被调函数中不必在参数前加"*"符
引用作为其它变量的别名而存在,因此在一些场合可以代替指针。C++主张用引用传递取代地址传递的方式,因为引用语法容易且不易出错。
cpp
#include <iostream>
using namespace std;
int getA1()
{
int a;
a = 10;
return a;
}
int& getA2()
{
int a;
a = 10;
return a;
}
int main(void)
{
int a1 = 0;
int a2 = 0;
//值拷⻉
a1 = getA1();
//将⼀个引⽤赋给⼀个变量,会有拷⻉动作
//理解: 编译器类似做了如下隐藏操作,a2 = *(getA2())
a2 = getA2();
//将⼀个引⽤赋给另⼀个引⽤作为初始值,由于是栈的引⽤,内存⾮法
int& a3 = getA2();
cout << "a1 = " << a1 << endl;
cout << "a2 = " << a2 << endl;
cout << "a3 = " << a3 << endl;
return 0;
}
cpp
#include <iostream>
using namespace std;
//函数当左值
//返回变量的值
int func1()
{
static int a1 = 10;
return a1;
}
//返回变量本⾝ ,
int& func2()
{
static int a2 = 10;
return a2;
}
int main(void)
{
//函数当右值
int c1 = func1();
cout << "c1 = " << c1 << endl;
int c2 = func2(); //函数返回值是⼀个引⽤,并且当右值
cout << "c2 = " << c2 << endl;
//函数当左值
//func1() = 100; //error
func2() = 100; //函数返回值是⼀个引⽤,并且当左值
c2 = func2();
cout << "c2 = " << c2 << endl;
return 0;
}
注意点:
- 不能返回局部变量的引用。
- 函数当左值,必须返回引用。【因为引用定义的函数,返回的是别名】
3.5 引用的本质:指针常量.(int* const ref)
引用的本质在c++内部实现是一个指针常量.
|------------------------------------------------------------|
| Type**&** ref = val**;** // Type* const ref = &val; |
c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见。
cpp
//发现是引用,转换为 int* const ref = &a;
void testFunc(int& ref){
ref = 100; // ref是引用,转换为*ref = 100
}
int main(){
int a = 10;
int& aRef = a; //自动转换为 int* const aRef = &a;这也能说明引用为什么必须初始化
aRef = 20; //内部发现aRef是引用,自动帮我们转换为: *aRef = 20;
cout << "a:" << a << endl;
cout << "aRef:" << aRef << endl;
testFunc(a);
return EXIT_SUCCESS;
}
3.6 指针引用
对于c++中的定义那个,语法清晰多了。函数参数变成指针的引用,用不着取得指针的地址。
cpp
struct Teacher{
int mAge;
};
//指针间接修改teacher的年龄
void AllocateAndInitByPointer(Teacher** teacher){
*teacher = (Teacher*)malloc(sizeof(Teacher));
(*teacher)->mAge = 200;
}
//引用修改teacher年龄
void AllocateAndInitByReference(Teacher*& teacher){
teacher->mAge = 300;
}
void test(){
//创建Teacher
Teacher* teacher = NULL;
//指针间接赋值
AllocateAndInitByPointer(&teacher);
cout << "AllocateAndInitByPointer:" << teacher->mAge << endl;
//引用赋值,将teacher本身传到ChangeAgeByReference函数中
AllocateAndInitByReference(teacher);
cout << "AllocateAndInitByReference:" << teacher->mAge << endl;
free(teacher);
}
cpp
#include <iostream>
using namespace std;
struct Teacher
{
char name[64];
int age;
};
//在被调⽤函数 获取资源
int getTeacher(Teacher * *p)
{
Teacher* tmp = NULL;
if (p == NULL)
{
return -1;
}
tmp = (Teacher*)malloc(sizeof(Teacher));
if (tmp == NULL)
{
return -2;
}
tmp->age = 33;
// p是实参的地址 *实参的地址 去间接的修改实参的值
*p = tmp;
return 0;
}
//指针的引⽤ 做函数参数
int getTeacher2(Teacher*& myp)
{
//给myp赋值 相当于给main函数中的pT1赋值
myp = (Teacher*)malloc(sizeof(Teacher));
if (myp == NULL)
{
return -1;
}
myp->age = 36;
return 0;
}
void FreeTeacher(Teacher* pT1)
{
if (pT1 == NULL)
{
return;
}
free(pT1);
}
int main(void)
{
Teacher* pT1 = NULL;
//1 c语⾔中的⼆级指针
getTeacher(&pT1);
cout << "age:" << pT1->age << endl;
FreeTeacher(pT1);
//2 c++中的引⽤ (指针的引⽤)
//引⽤的本质 间接赋值后2个条件 让c++编译器帮我们程序员做了。
getTeacher2(pT1);
cout << "age:" << pT1->age << endl;
FreeTeacher(pT1);
return 0;
}
3.7 常量引用(const)
常量引用的定义格式:
|-------------------------------------|
| const Type**&** ref = val**;** |
注意:
- 如果想对一个常量进行引用, 必须是一个const引用。
- const修饰的引用,不能修改。
cpp
void test01(){
int a = 100;
const int& aRef = a; //此时aRef就是a
//aRef = 200; 不能通过aRef的值
a = 120; //OK
cout << "a:" << a << endl;//120
cout << "aRef:" << aRef << endl;//120
}
void test02(){
//不能把一个字面量赋给引用
//int& ref = 100;
//但是可以把一个字面量赋给常引用
//因为const int的级别比int高
const int& ref = 100; //int temp = 200; const int& ret = temp;
}
[const 引用使用场景]
常量引用主要用在函数的形参,尤其是类的拷贝/复制构造函数。
将函数的形参定义为常量引用的好处:
- 引用不产生新的变量,减少形参与实参传递时的开销。
- 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。
如果希望实参随着形参的改变而改变,那么使用一般的引用,如果不希望实参随着形参改变,那么使用常引用。
cpp
//const int& param防止函数中意外修改数据
void ShowVal(const int& param){
cout << "param:" << param << endl;
}
3.8 例子
cpp
#include <iostream>
using namespace std;
int main(void)
{
int a = 10; //c编译器分配4个字节内存, a内存空间的别名
int& b = a; //b就是a的别名-->const * int b=a
a = 11; //直接赋值
{
int* p = &a;
*p = 12;
cout << a << endl;//12
}
b = 14;
cout << "a = " << a << ", b = " << b << endl;//14 14
return 0;
}
结论:可以通过普通参数修改引用,但是不能通过引用修改参数
cpp
#include <iostream>
using namespace std;
int main() {
int a = 20;
int& ref = a;
ref = 30;
cout << ref << a << endl;//30 30
int b = 40;
const int& ref2 = b;
b = 100;
cout << ref2 << b << endl;//100 100
int& rref = ref;
rref = 200;
cout << rref << ref << a << endl;//200 200 200
}
作业:
1.简述C++中命名空间的作用。
namespace AAA{
int a;
}
cpp
using AAA::a;
AAA::a;
using namespace AAA;
2. 定义两个命名空间A 和 B 分别在A中和B中定义变量value
cpp
#include<iostream>
using namespace std;
namespace A {
int a = 10;
}
namespace B {
int b = 20;
}
int main() {
using namespace A;
using namespace B;
cout << A::a << endl;
cout << B::b << endl;
}
3. C语言的三目运算符 ? : , 可以当左值么? C++的是否可以? 为什么?
c语言的三目运算符不可以当左值,因为返回的是一个常量。
c++的三目运算符可以做左值
4. 下面哪条语句是错误的?为什么?
(1 < 2? a: b) = 100;//正确,因为结果是一个变量,可以被赋值
(1 < 2? 10: 20) = 100//错误,因为结果是一个常量,常量不能被常量赋值
5. const int a; 在C++编译器中是否需要初始化,为什么?
在c++中,一旦声明一个const变量,则在声明的过程中一定要对起进行初始化
6.temp
cpp
int main() {
int a = 10;
int* p = (int*)&a;
//*p:实际上修改的是temp的值
*p = 20;
cout << a << endl;//20
cout << *p << endl;//20
}
6. 简述引用的特点?
1.引用本身不占用内存空间,与被引用对象共用内存
2.引用定义时,必须初始化
3.引用的类型必须与被引用对象的类型保持一致 比如:int a; 引用必须使用int&
4.引用只能引用一个对象,一个对象可以被多次引用。
7.判断
cpp
int main() {
int a = 20;
const int& re_a = a;
re_a = 30;//错误,不能直接修改const变量
}
4.inline内联函数
https://www.cnblogs.com/chengxuyuancc/archive/2013/04/04/2999844.html
4.1 内联函数的引出
c++从c中继承的一个重要特征就是效率。假如c++的效率明显低于c的效率,那么就会有很大的一批程序员不去使用c++了。
4.2 预处理宏的缺陷
宏函数是预处理器进行的
内联的编译器处理的
预处理器宏存在问题的关键是我们可能认为预处理器的行为和编译器的行为是一样的。当然也是由于宏函数调用和函数调用在外表看起来是一样的,因为也容易被混淆。但是其中也会有一些微妙的问题出现:
问题一:
cpp
#define ADD(x,y) x+y
inline int Add(int x,int y){
return x + y;
}
void test(){
int ret1 = ADD(10, 20) * 10; //希望的结果是300
int ret2 = Add(10, 20) * 10; //希望结果也是300
cout << "ret1:" << ret1 << endl; //210
cout << "ret2:" << ret2 << endl; //300
}
问题二:
cpp
#define COMPARE(x,y) ((x) < (y) ? (x) : (y))
int Compare(int x,int y){
return x < y ? x : y;
}
void test02(){
int a = 1;
int b = 3;
//cout << "COMPARE(++a, b):" << COMPARE(++a, b) << endl; // 3
cout << "Compare(int x,int y):" << Compare(++a, b) << endl; //2
}
4.3 内联函数基本概念:空间换时间
在c++中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数 。内联函数具有普通函数的所有行为。**唯一不同之处在于内联函数会在适当的地方像预定义宏一样展开,所以不需要函数调用的开销。**因此应该不使用宏,使用内联函数。
注意必须函数体和声明结合在一起,否则编译器将它作为普通函数来对待。
cpp
inline void func(int a);
inline int func(int a) {
return a;
}
内联函数的确占用空间,但是内联函数相对于普通函数的优势只是省去了函数调用时候的压栈,跳转,返回的开销。我们可以理解为内联函数是以空间换时间。
4.4 类内部的内联函数
为了定义内联函数,通常必须在函数定义前面放一个inline关键字。但是在类内部定义内联函数时并不是必须的。任何在类内部定义的函数自动成为内联函数。
cpp
class Person{
public:
Person(){ cout << "构造函数!" << endl; }
void PrintPerson(){ cout << "输出Person!" << endl; }
}
4.5 内联函数和编译器
4.6 注意点
- 不能存在任何形式的循环语句
- 不能存在过多的条件判断语句
- 函数体不能过于庞大
- 不能对函数进行取址操作
内联仅仅只是给编译器一个建议,编译器不一定会接受这种建议,如果你没有将函数声明为内联函数,那么编译器也可能将此函数做内联编译。一个好的编译器将会内联小的、简单的函数。
4.7 内联函数 VS 宏函数
4.8 总结
5. 函数的默认参数和占位函数
5.1 默认参数
c++在声明函数原型的时可为一个或者多个参数指定默认(缺省)的参数值,当函数调用的时候如果没有指定这个值,编译器会自动用默认值代替。
cpp
void TestFunc01(int a = 10, int b = 20){
cout << "a + b = " << a + b << endl;
}
//注意点:
//1. 形参b设置默认参数值,那么后面位置的形参c也需要设置默认参数
void TestFunc02(int a,int b = 10,int c = 10){}
//2. 如果函数声明和函数定义分开,函数声明设置了默认参数,函数定义不能再设置默认参数
void TestFunc03(int a = 0,int b = 0);
void TestFunc03(int a, int b){}
int main(){
//1.如果没有传参数,那么使用默认参数
TestFunc01();
//2. 如果传一个参数,那么第二个参数使用默认参数
TestFunc01(100);
//3. 如果传入两个参数,那么两个参数都使用我们传入的参数
TestFunc01(100, 200);
return EXIT_SUCCESS;
}
注意点:
5.2 函数的占位参数
c++在声明函数时,可以设置占位参数。占位参数只有参数类型声明,而没有参数名声明。一般情况下,在函数体内部无法使用占位参数。【占位符也是参数,必须传参数】
cpp
void TestFunc01(int a,int b,int){
//函数内部无法使用占位参数
cout << "a + b = " << a + b << endl;
}
//占位参数也可以设置默认值
void TestFunc02(int a, int b, int = 20){
//函数内部依旧无法使用占位参数
cout << "a + b = " << a + b << endl;
}
int main(){
//错误调用,占位参数也是参数,必须传参数
//TestFunc01(10,20);
//正确调用
TestFunc01(10,20,30);
//正确调用
TestFunc02(10,20);
//正确调用
TestFunc02(10, 20, 30);
return EXIT_SUCCESS;
}
6. 函数重载(overload)
6.1 函数重载概述
|--------------------------------|
| 能使名字方便使用,是任何程序设计语言的一个重要特征! |
在传统c语言中,函数名必须是唯一的,程序中不允许出现同名的函数。在c++中是允许出现同名的函数,这种现象称为函数重载。
函数重载的目的就是为了方便的使用函数名。
6.2 函数重载基本语法
实现函数重载的条件:
|------------------------------------------------------------|
| 1. 同一个作用域 2. 参数个数不同 3. 参数类型不同 4. 参数顺序不同 5. 返回值并不是构成函数重载的条件 |
cpp
//1. 函数重载条件
namespace A{
void MyFunc(){ cout << "无参数!" << endl; }
void MyFunc(int a){ cout << "a: " << a << endl; }
void MyFunc(string b){ cout << "b: " << b << endl; }
void MyFunc(int a, string b){ cout << "a: " << a << " b:" << b << endl;}
void MyFunc(string b, int a){cout << "a: " << a << " b:" << b << endl;}
}
//2.返回值不作为函数重载依据
namespace B{
void MyFunc(string b, int a){}
//int MyFunc(string b, int a){} //无法重载仅按返回值区分的函数
}
注意: 函数重载和默认参数一起使用,需要额外注意二义性问题的产生。
为什么函数返回值不作为重载条件呢?
调用准则
cpp
#include <iostream>
using namespace std;
void print(double a) {
cout << a << endl;
}
void print(int a) {
cout << a << endl;
}
int main()
{
print(1); // print(int)
print(1.1); // print(double)
print('a'); // print(int)
print(1.11f); // print(double)
return 0;
}
编译器调用重载函数的准则:
6.3 重载底层实现(name mangling)
6.4 函数重载与函数默认参数
6.5 函数重载和函数指针
函数指针基本语法
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
int func(int a, int b)
{
cout << "func(int, int)" << endl;
return 0;
}
int func(int a, int b, int c)
{
cout << "func(int, int,int )" << endl;
return 0;
}
//1 . 定义一种函数类型
typedef int(MY_FUNC)(int, int);
//2 顶一个指向之中函数类型的指针类型
typedef int(*MY_FUNC_P)(int, int);
int main(void)
{
//1.
MY_FUNC *fp = NULL;
fp = func;
fp(10, 20);
//2.
MY_FUNC_P fp1 = NULL;
fp1 = func;
fp1(10, 20);
//3.
int(*fp3)(int, int) = NULL;
fp3 = func;
fp3(10, 20);
func(10, 20);
func(10, 20, 30);
fp3 = func; //fp3 ---> func(int,int)
//实际上在给函数指针赋值的时候,是会发生函数重载匹配的
//在调用函数指针的时候,所调用的函数就已经固定了。
int(*fp4)(int, int, int) = NULL;
fp4 = func; //fp4 ---> func(int,int,int)
fp3(10, 30);//func(int,int)
fp3(10, 20);
fp4(10, 30, 30);
return 0;
}
6.6 总结
7.类和对象
7.1 类和对象的基本概念
C和C++中struct区别
- c语言struct只有变量
- c++语言struct 既有变量,也有函数
访问控制权限
一个类类的内部,是默认private
我们编写程序的目的是为了解决现实中的问题,而这些问题的构成都是由各种事物组成,我们在计算机中要解决这种问题,首先要做就是要将这个问题的参与者:事和物抽象到计算机程序中,也就是用程序语言表示现实的事物。
cpp
//封装两层含义
//1. 属性和行为合成一个整体
//2. 访问控制,现实事物本身有些属性和行为是不对外开放
class Person{
//人具有的行为(函数)
public:
void Dese(){ cout << "我有钱,年轻,个子又高,就爱嘚瑟!" << endl;}
//人的属性(变量)
public:
int mTall; //多高,可以让外人知道
protected:
int mMoney; // 有多少钱,只能儿子孙子知道
private:
int mAge; //年龄,不想让外人知道
};
int main(){
Person p;
p.mTall = 220;
//p.mMoney 保护成员外部无法访问
//p.mAge 私有成员外部无法访问
p.Dese();
return EXIT_SUCCESS;
}
sruct和class的区别
cpp
class A{
int mAge;
};
struct B{
int mAge;
};
void test(){
A a;
B b;
//a.mAge; //无法访问私有成员
b.mAge; //可正常外部访问
}
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
struct Hero
{
char name[64];
int sex;
};
void printHero(struct Hero& h)
{
cout << "Hero" << endl;
cout << "name = " << h.name << endl;
cout << "sex = " << h.sex << endl;
}
class AdvHero
{
public://访问控制权限
char name[64];
int sex;
void printHero()
{
cout << "advHero" << endl;
cout << "name = " << name << endl;
cout << "sex = " << sex << endl;
}
};
class Animal
{
//{}以内 叫类的内部, 以外叫类的外部
public:
char kind[64];
char color[64];
//在public下面定义成员变量和函数 是能够在类的内部和外部都可以访问的。
void printAnimal()
{
cout << "kind = " << kind << endl;
cout << "color = " << color << endl;
}
void write()
{
cout << kind << "开始鞋子了" << endl;
}
void run()
{
cout << kind << "跑起来了" << endl;
}
//
private:
//在private下面定义的成员变量和方法只能够在类的内部访问
};
int main(void)
{
Hero h;
strcpy(h.name, "gailun");
h.sex = 1;
printHero(h);
AdvHero advH;
strcpy(advH.name, "ChunBro");
advH.sex = 1;
advH.printHero();
cout << "-----------" << endl;
Animal dog;
strcpy(dog.kind, "dog");
strcpy(dog.color, "yellow");
Animal sheep;
strcpy(sheep.kind, "sheep");
strcpy(sheep.color, "white");
dog.write();
sheep.run();
return 0;
}
将成员变量设置为private
cpp
class AccessLevels{
public:
//对只读属性进行只读访问
int getReadOnly(){ return readOnly; }
//对读写属性进行读写访问
void setReadWrite(int val){ readWrite = val; }
int getReadWrite(){ return readWrite; }
//对只写属性进行只写访问
void setWriteOnly(int val){ writeOnly = val; }
private:
int readOnly; //对外只读访问
int noAccess; //外部不可访问
int readWrite; //读写访问
int writeOnly; //只写访问
};
7.2 用class去封装带行为的类
class 封装的本质,在于将数据和行为,绑定在一起然后能过对象来完成操 作。
cpp
#include <iostream>
using namespace std;
class Date
{
public:
void init(Date & d);
void print(Date& d);
bool isLeapYear(Date& d);
private:
int year;
int month;
int day;
};
void Date::init(Date& d)
{
cout << "year,month,day:" << endl;
cin >> d.year >> d.month >> d.day;
}
void Date::print(Date& d)
{
cout << "year month day" << endl;
cout << d.year << ":" << d.month << ":" << d.day << endl;
}
bool Date::isLeapYear(Date& d)
{
if ((d.year % 4 == 0 && d.year % 100 != 0) || d.year % 400 == 0)
return true;
else
return false;
}
int main()
{
Date d;
d.init(d);
d.print(d);
if (d.isLeapYear(d))
cout << "leap year" << endl;
else
cout << "not leap year" << endl;
return 0;
}
7.3 类的封装
一般类中的属性是private
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
struct Date
{
int year;
int month;
int day;
};
void init_date(struct Date & d)
{
cout << "year, month, day" << endl;
cin >> d.year;
cin >> d.month;
cin >> d.day;
}
//打印data的接口
void print_date(struct Date &d)
{
cout << d.year << "年" << d.month << "月" << d.day << "日" << endl;
}
bool is_leap_year(struct Date &d)
{
if (((d.year % 4 == 0) && (d.year % 100 != 0)) || (d.year % 400 == 0)) {
return true;
}
return false;
}
class MyDate
{
public:
//成员方法 成员函数
void init_date()
{
cout << "year, month, day" << endl;
cin >> year;
cin >> month;
cin >> day;
}
//打印data的接口
void print_date()
{
cout << year << "年" << month << "月" << day << "日" << endl;
}
bool is_leap_year()
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
return true;
}
return false;
}
int get_year()
{
return year;
}
void set_year(int new_year)
{
year = new_year;
}
protected://保护控制权限。在类的继承中跟private有区别,在单个类中,跟private是一抹一样。
private:
int year;
int month;
int day;
};
//一个类类的内部,默认的访问控制权限是private
class Hero
{
int year;
};
//一个结构体默认的访问控制权限的是public
struct Hero2
{
int year;
void print()
{
}
};
int main(void)
{
#if 0
Date d1;
init_date(d1);
print_date(d1);
if (is_leap_year(d1) == true) {
cout << "是闰年 " << endl;
}
else {
cout << "不是闰年 " << endl;
}
#endif
MyDate my_date;
my_date.init_date();
my_date.print_date();
if (my_date.is_leap_year() == true)
{
cout << "是闰年 " << endl;
}
else {
cout << "不是闰年 " << endl;
}
//getter,setter
cout << my_date.get_year() << endl;
my_date.set_year(2000);
cout << my_date.get_year() << endl;
Hero h;
//h.year = 1000;
Hero2 h2;
h2.year = 100;
return 0;
}
7.4 ⾯向对象编程
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
//面向对象
class Dog
{
public:
void eat(char *food)
{
cout << name << "吃" << food << endl;
}
char name[64];
};
//面向过程
void eat(class Dog &dog, char *food)
{
cout << dog.name << "吃" << food << endl;
}
int main(void)
{
Dog dog;
strcpy(dog.name, "狗");
eat(dog, "翔");
dog.eat("翔");
return 0;
}
求圆的面积和周长
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
//圆的周长
double getCircleGirth(double r)
{
return 2 * 3.14 * r;
}
//源的面积
double getCircleArea(double r)
{
return 3.14 * r * r;
}
//用面向对象实现
//圆类
class Circle
{
public:
void setR(double r)
{
m_r = r;
}
double getR()
{
return m_r;
}
double getGirth()
{
return 2 * 3.14 * m_r;
}
double getArea()
{
return m_r * m_r * 3.14;
}
private:
double m_r; //圆的私有成员 半径
};
class Circle2
{
public:
void setR(double r)
{
m_r = r;
}
double getR()
{
return m_r;
}
double getArea()
{
m_area = m_r * m_r * 3.14;
return m_area;
}
double getGirth()
{
m_girth = m_r * 2 * 3.14;
return m_girth;
}
private:
double m_r;
double m_girth; //周长
double m_area;//面积
};
int main(void)
{
double r = 10; //圆的半径
double g = 0;
double a = 0;
g = getCircleGirth(r);
a = getCircleArea(r);
cout << "圆的半径是" << r << endl;
cout << "圆的周长是" << g << endl;
cout << "圆的面积是" << a << endl;
cout << "------" << endl;
Circle c;
c.setR(10);
cout << "圆的半径是" << c.getR() << endl;
cout << "圆的周长是" << c.getGirth() << endl;
cout << "圆的面积是" << c.getArea() << endl;
cout << "------------" << endl;
Circle2 c2;
c2.setR(10);
cout << "圆的半径是" << c2.getR() << endl;
cout << "圆的周长是" << c2.getGirth() << endl;
cout << "圆的面积是" << c2.getArea() << endl;
return 0;
}
圆的周长和面积多文件编写
Circle.h
cpp
#pragma once
#if 0
#ifndef __CIRCLE_H_
#define __CIRCLE_H_
#endif
#endif
class Circle
{
public:
//设置半径:
void setR(double r);
//得到半径
double getR();
double getArea();
double getGirth();
private:
double m_r;
double m_area;
double m_girth;
};
Circle.cpp
cpp
#include "Circle.h"
//Circle::表示还在圆的内部
void Circle::setR(double r)
{
m_r = r;
}
double Circle::getR()
{
return m_r;
}
double Circle::getArea()
{
m_area = m_r *m_r *3.14;
return m_area;
}
double Circle::getGirth()
{
m_girth = m_r * 2 * 3.14;
return m_girth;
}
mian.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "Circle.h"
using namespace std;
int main(void)
{
Circle c;
c.setR(10);
cout << "面积" << c.getArea() << endl;
return 0;
}
判断两个立方体是否相等
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
//立方体类
class Cube
{
public:
void setABC(int a, int b, int c)
{
m_a = a;
m_b = b;
m_c = c;
}
int getArea()
{
return (m_a*m_b) * 2 + (m_a*m_c) * 2 + (m_b*m_c) * 2;
}
int getVolume()
{
return (m_a*m_b*m_c);
}
int getA()
{
return m_a;
}
int getB()
{
return m_b;
}
int getC()
{
return m_c;
}
//同类之间无私处
bool judgeCube(Cube &another)
{
if (m_a == another.m_a &&
m_b == another.getB() &&
m_c == another.getC()) {
return true;
}
else {
return false;
}
}
private:
int m_a;
int m_b;
int m_c;
};
//全局函数
bool judgeCube(Cube &c1, Cube &c2)
{
if (c1.getA() == c2.getA() &&
c1.getB() == c2.getB() &&
c1.getC() == c2.getC()) {
return true;
}
else {
return false;
}
}
int main(void)
{
Cube c1;
c1.setABC(10, 20, 30);
Cube c2;
c2.setABC(10, 20, 30);
cout << "c1 的体积是" << c1.getVolume() << endl;
cout << "c1 的面积是" << c1.getArea() << endl;
if (judgeCube(c1, c2) == true) {
cout << "相等" << endl;
}
else {
cout << "不相等" << endl;
}
cout << " ------ " << endl;
if (c1.judgeCube(c2) == true) {
cout << "相等" << endl;
}
else {
cout << "不相等" << endl;
}
return 0;
}
判断点是否在圆的内部
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
//点类
class Point
{
public:
void setXY(int x, int y)
{
m_x = x;
m_y = y;
}
int getX()
{
return m_x;
}
int getY()
{
return m_y;
}
private:
int m_x;
int m_y;
};
//圆类
class Circle
{
public:
void setXY(int x, int y)
{
x0 = x;
y0 = y;
}
void setR(int r)
{
m_r = r;
}
//提供一个判断点是否在圆内
//true 在内部
//false 在外部
bool judgePoint(Point &p)
{
int dd;
dd = (p.getX() - x0)*(p.getX() - x0) + (p.getY() - y0)*(p.getY() - y0);
if (dd > m_r*m_r) {
return false;
}
else {
return true;
}
}
private:
int x0;
int y0;
int m_r;
};
int main(void)
{
Circle c;
c.setXY(2, 2);
c.setR(4);
Point p;
p.setXY(8, 8);
if (c.judgePoint(p) == true) {
cout << "圆的内部" << endl;
}
else {
cout << "圆的外部" << endl;
}
return 0;
}
判断两个圆是否相交
cpp
#include<iostream>
using namespace std;
class Point {
public:
void setXY(int x, int y) {
m_x = x;
m_y = y;
}
//计算两个点之间的距离
double pointDistance(Point& another) {
int dis_x = m_x - another.m_x;
int dis_y = m_y - another.m_y;
//斜边
double dis = sqrt(dis_x * dis_x + dis_y * dis_y);
return dis;
}
private:
int m_x;
int m_y;
};
class Circle {
public:
void setR(int r) {
m_r = r;
}
void setXY(int x1, int y1) {
p0.setXY(x1, y1);
}
//判断圆是否相交
bool isConnection(Circle& another) {
//两个圆的半径之和
int r = m_r + another.m_r;
//两个圆心之间的距离
double dis = p0.pointDistance(another.p0);
if (dis <= r) {
//相交
return true;
}
else {
return false;
}
}
private:
int m_r;
Point p0;
};
int main(void)
{
Circle c1, c2;
int x, y, r;
cout << "请输入第一个圆的半径" << endl;
cin >> r;
c1.setR(r);
cout << "请输入第一个圆的x" << endl;
cin >> x;
cout << "请输入第一个圆的y" << endl;
cin >> y;
c1.setXY(x, y);
cout << "请输入第2个圆的半径" << endl;
cin >> r;
c2.setR(r);
cout << "请输入第2个圆的x" << endl;
cin >> x;
cout << "请输入第2个圆的y" << endl;
cin >> y;
c2.setXY(x, y);
if (c1.isConnection(c2) == true) {
cout << "相交" << endl;
}
else {
cout << "不想交" << endl;
}
return 0;
}