小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
一、为什么学习C++
C++是C语言的升级版,在C语言的基础上添加了面向对象的思想,C++既可以底层编程,也可以图形界面编程,也可以数据处理,因为是面向对象的编程,所以在写代码时大大提高了效率。
二、相关概念
C语言是面向过程的语言,C++是面向对象的语言
- 面向过程:编程思想注重于过程,意味着大大小小的功能都需要去实现
- 面向对象:编程思想注重于结果,在完成一个大的项目的时候,小的功能基本都已经实现了,所以只需要进行调用就可以实现大的功能,所以便于程序员开发。它的主要功能是可以更方便得编写出好程序,让每个程序员更加快乐”。
三、C++重要的概念
3.1 面向过程
面向过程是一种以过程为中心的编程思想。
通过分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向过程编程思想的核心:功能分解,自顶向下,逐层细化(程序=数据结构+算法)。
面向过程编程语言存在的主要缺点是不符合人的思维习惯,而是要用计算 机的思维方式去处理问题,而且面向过程编程语言重用性低,维护困难。
3.2 面向对象
面向对象编程(Object-Oriented Programming)简称 OOP 技术,是开发计算机应用程序的一种新方法、新思想。过去的面向过程编程常常会导致所有的代码都包含在几个模块中,使程序难以阅读和维护。在做一些修改时常常牵一动百,使以后的开发和维护难以为继。而使用 OOP 技术,常常要使用许多代码模块,每个模块都只提供特定的功能,它们是彼此独立的,这样就增大了代码重用的几率,更加有利于软件的开发、维护和升级。
在面向对象中,算法与数据结构被看做是一个整体,称作对象,现实世界中任何类的对象都具有一定的属性和操作,也总能用数据结构与算法两者合一地来描述,所以可以用下面的等式来定义对象和程序:
- 对象 = 算法 + 数据结构
- 程序 = 对象 + 对象 + ……
从上面的等式可以看出,程序就是许多对象在计算机中相继表现自己,而对象则是一个个程序实体。 面向对象编程思想的核心:应对变化,提高复用。
3.3 类和对象
类:类似于结构体,一个类转给你函数和变量都可以定义 例如数据结构中的链表,C语言是需要写一个程序来实现和调用, C++可以直接将所有的变量和功能函数都定义在一个类中,这样调 用起来更加方便
对象:类实例化的对象,类似于结构体变量,所以对象就是类定义的变量
3.4 面向对象语言三要素
==封装==:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让 可信的类或者对象操作,对不可信的进行信息隐藏。
类将成员变量和成员函数封装在类的内部,根据需要设置访问权限, 通过成员函数管理内部状态。
C语言中结构体内部不可以定义函数,但是在C++的结构体或者类重视可以定义函数的
==继承==:继承所表达的是类之间相关的关系,这种关系使得对象可以继承另 外一类对象的特征和能力。
继承的作用:避免公用代码的重复开发,减少代码和数据冗余
一个类可以基础另一个类中几乎所有的成员函数和成员变量,这样大大提高了代码的复用性
==多态==:多态性可以简单地概括为“一个接口,多种方法”,字面意思为多 种形态。程序在运行时才决定调用的函数,它是面向对象编程领 域的核心概念。
函数调用之前,一般在编译阶段函数的地址就已经确定了, 称之为静态多态,如果是在代码运行的时候才确定,称之为动态多态
静态多态:函数重载、运算符重载
动态多态:虚函数、纯虚函数、虚析构函数、纯虚析构函数
四、域解析符
#include <iostream>
using namespace std;
int num = 100;
void test1()
{
cout << "num = " << num << endl;
int num = 888;
cout << "num = " << num << endl;
//:: :域解析符,主要用于操作全局定义的变量或者类或者函数等等
cout << "num = " << ::num << endl;
::num = 999;
cout << "num = " << ::num << endl;
}
int main()
{
test1();
return 0;
}
五、命名空间
- 主要就是为了防止多人一起协同写代码时出现重名的情况
- 命名空间里面可以存放几乎所有全局的东西,例如全局变量、函数、结构体、类、宏定义、取别名
5.1 定义一个命名空间
注意:**定义之后不需要加==;==**内部的变量空间都已经开辟了,不是在定义数据类型
namespace zhangsan {
int num = 333;
void myfun()
{
cout << "nihao, beijing" << endl;
}
}
5.2 命名空间的基本使用
命名空间里面的成员需要命名空间的名字通过域解析符来操作
void test1()
{
cout << "zhangsan: num = " << zhangsan::num << endl;
cout << "lisi: num = " << lisi::num << endl;
zhangsan::myfun();
lisi::myfun();
}
如果在一个函数里面要经常使用某个命名空间内的成员,可以通过using关键字标识,这样操作这个成员的时候就不需要命名空间的名字来访问了;在一个函数里面如果已经对某一个命名空间中的成员通过using标识,就不能在对同名的其他命名空间的成员进行标识
using zhangsan::num;
cout << "zhangsan: num = " << num << endl;
num = 1000;
如果在一个代码中都要使用某一个命名空间中的成员,可以通过using标识整个命名空间,这样里面所有的成员操作的时候都不需要通过命名空间来访问了,一般都放在最上面
#include <iostream>//程序与外部的通信
using namespace std;
namespace zhangsan {
int num = 333;
void myfun()
{
cout << "nihao, beijing" << endl;
}
}
namespace lisi {
int num = 888;
void myfun()
{
cout << "欢迎来到华清远见学习" << endl;
}
}
//命名空间的基本使用
//命名空间里面的成员需要命名空间的名字通过域解析符来操作
void test1()
{
cout << "zhangsan: num = " << zhangsan::num << endl;
cout << "lisi: num = " << lisi::num << endl;
zhangsan::myfun();
lisi::myfun();
}
void test2()
{
//如果在一个函数里面要经常使用某个命名空间内的成员
//可以通过using关键字标识,这样操作这个成员的时候就
//不需要命名空间的名字来访问了
using zhangsan::num;
cout << "zhangsan: num = " << num << endl;
num = 1000;
cout << "zhangsan: num = " << num << endl;
//在一个函数里面如果已经对某一个命名空间中的成员通过using标识,
//就不能在对同名的其他命名空间的成员进行标识
//using lisi::num;
cout << "lisi: num = " << lisi::num << endl;
using lisi::myfun;
myfun();
}
//如果在一个代码中都要使用某一个命名空间中的成员,
//可以通过using标识整个命名空间,这样里面所有的成员
//操作的时候都不需要通过命名空间来访问了,一般都放在最上面
using namespace zhangsan;
void test3()
{
cout << num << endl;
myfun();
zhangsan::myfun();
lisi::myfun();
}
int main()
{
test3();
return 0;
}
六、C++中的结构体的使用
6.1 C语言中的结构体
#include <stdio.h>
//定义一个结构体
struct Chinese{
char name[32];
//C语言结构体不允许内部定义函数
#if 0
void myfun()
{
printf("hello world\n");
}
#endif
};
struct USA{
char name[32];
};
void IntroduceChinese(struct Chinese *ch)
{
printf("%s是一个中国人\n", ch->name);
}
void IntroduceUSA(struct USA *u)
{
printf("%s is a foreigner\n", u->name);
}
void test1()
{
//定义结构体变量
//C语言定义结构体变量时必须加struct
struct Chinese ch = {"张三"};
struct USA u = {"Bob"};
IntroduceChinese(&ch);
IntroduceUSA(&u);
//C语言中的结构体没有封装性,所以函数无法再结构体中定义,
//意味着这个函数谁都可以调用,有时候就会产生歧义
IntroduceChinese(&u);
}
int main()
{
test1();
return 0;
}
6.2 C++中的结构体
#include <iostream>
using namespace std;
struct Chinese{
char name[32];
//C++中的结构体内部是可以定义函数的
#if 0
void fun()
{
cout << "hello world" << endl;
}
#endif
void Introduce()
{
printf("%s是一个中国人\n", name);
}
};
struct USA{
char name[32];
void Introduce()
{
printf("%s is a foreigner\n", name);
}
};
void test1()
{
//C++中结构体变量定义的时候可以不用加struct
Chinese ch = {"张三"};
USA u = {"Bob"};
//C++中结构体具有封装性,一定程度上保护了自己的成员
//意味着有些函数只允许自己使用,不允许其他结构体的变
//量操作
ch.Introduce();
u.Introduce();
}
int main()
{
test1();
return 0;
}
6.3 区别对比
- C语言结构体不允许内部定义函数《===》C++中的结构体内部是可以定义函数的
- C语言定义结构体变量时必须加
struct《===》C++中结构体变量定义的时候可以不用加struct - C语言中的结构体没有封装性,所以函数无法再结构体中定义,意味着这个函数谁都可以调用,有时候就会产生歧义《===》C++中结构体具有封装性,一定程度上保护了自己的成员,意味着有些函数只允许自己使用,不允许其他结构体对变量操作
七、bool类型
#include <iostream>
using namespace std;
//C++支持bool类型,可以赋值为true或者false,大小占一个字节
int main()
{
bool a = true;
cout << "a = " << a << endl;
bool b = false;
cout << "b = " << b << endl;
cout << "sizeof(a) = " << sizeof(a) << endl;
return 0;
}
八、C++中的标准输入输出
#include <iostream>
using namespace std;
//标准输出:cout
//cout不是一个函数,是类实例化的对象
void test1()
{
int a = 333;
char b = 'w';
char c[] = "hello world";
float d = 3.1415926f;
cout << "a = " << a << endl;
//输出十六进制
cout << hex << a << endl;
//输出八进制
cout << oct << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
cout << "d = " << d << endl;
}
//标准输入:cin
//cin是一个类实例化的对象
void test2()
{
// int a;
// cin >> a;
// cout << "a = " << a << endl;
int a, b, c;
cin >> a >> b >> c;
cout << "a = " << a;
cout << ", b = " << b;
cout << ", c = " << c << endl;
}
int main()
{
test2();
return 0;
}
九、C++中的const
9.1 C语言的const
- const修饰全局变量:不管用哪种方式都无法修改全局变量的值,只能使用不能修改
- const修饰局部变量:无法通过变量本身直接修改,但是可以通过指针修改地址里面的内容
- const修饰指针变量的类型,无法通过指针变量修改地址里面的内容
- const修饰指针变量,无法修改指针变量保存的地址
#include <stdio.h>
//const修饰全局变量
//不管用哪种方式都无法修改全局变量的值,只能使用不能修改
const int a = 888;
void test1()
{
printf("a = %d\n", a);
//a = 666;//这样肯定不行
//printf("a = %d\n", a);
//int *p = &a;
//*p = 666;
//printf("a = %d\n", a);//程序异常结束,因为信号
}
//const修饰局部变量
//无法通过变量本身直接修改,但是可以通过指针修改地址里面的内容
void test2()
{
const int num = 100;
printf("num = %d\n", num);
//num = 200;//只读,不能改
//printf("num = %d\n", num);
int *p = #//通过地址修改了
*p = 300;
printf("num = %d\n", num);
}
//const修饰指针变量或者指针变量的类型
void test3()
{
int n = 100;
int *p = &n;
printf("*p = %d\n", *p);
//修改*p里面所保存的值
n = 200;//通过值来修改
printf("*p = %d\n", *p);
//const修饰指针变量的类型,无法通过指针变量修改地址里面的内容
//如果const int *p = &n;这里会报错
*p = 300;//通过*p自己来修改
printf("*p = %d\n", *p);
//const修饰指针变量,无法修改指针变量保存的地址
//int * const p = &n;
int m = 400;//让p重新再保存一块地址
p = &m;
printf("*p = %d\n", *p);
}
int main()
{
test3();
return 0;
}
9.2 C++中的const
C++中const修饰全局变量或者指针变量以及指针变量的类型跟C语言是一样的,唯独修饰局部变量时与C语言有区别
- 当局部变量用一个常量初始化时,如果用const修饰,会将其保存在符号常量表中
- 如果用volatile修饰,说明这个变量是易变的,不会保存在符号常量表中
- const修饰的变量如果同另一个变量初始化,则不会将其保存在符号常量表中
#include <iostream>
using namespace std;
void test1()
{
//当局部变量用一个常量初始化的时候,如果用const修饰,会将其保存在符号常量表中
const int a = 100;
//如果用volatile修饰,说明这个变量是易变的,不会保存在符号常量表中
//volatile const int a = 100;
cout << "a = " << a << endl;
//a = 200;//不能通过自己本身修改
//cout << "a = " << a << endl;
//注意:C++对于类型优严格要求,类型必须一致否则报错
int *p = (int *)&a;
*p = 300;
cout << "a = " << a << endl;
cout << "*p = " << *p << endl;
cout << "&a = " << &a << endl;
cout << "p = " << p << endl;
}
void test2()
{
int a = 100;
//const修饰的变量如果同另一个变量初始化,则不会将其保存在符号常量表中
const int b = a;
cout << "b = " << b << endl;
int *p = (int *)&b;
*p = 333;
cout << "b = " << b << endl;
}
struct Msg{
int a;
int b;
char c;
};
void test3()
{
const Msg g = {100, 200, 'w'};
cout << g.a << ", ";
cout << g.b << ", ";
cout << g.c << endl;
//没有保存再符号常量表
Msg *p = (Msg *)&g;
p->a = 666;
p->b = 888;
p->c = 'p';
cout << g.a << ", ";
cout << g.b << ", ";
cout << g.c << endl;
}
int main()
{
test3();
return 0;
}
9.3 const和#define区别总结:
- const有类型,可进行编译器类型安全检查。#define无类型,不可进行类型检查.
- const有作用域,而#define不重视作用域,默认定义处到文件结尾.如果定义在指定作用域下有效的常量,那么#define就不能用。
十、inline内联函数
内联函数就是用inline修饰一个函数,将这个函数称之为内联函数
在类中定义的成员函数全部默认为内联函数
内联函数类似宏函数,使用效率比起普通函数要高,在c++中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。内联函数具有普通函数的所有行为。唯一不同之处在于内联函数会在适当的地方像预定义宏一样展开,所以不需要函数调用的开销。因此应该不使用宏,使用内联函数。
在普通函数(非成员函数)函数前面加上inline关键字使之成为内联函数。但是必须注意必须函数体和声明结合在一起,否则编译器将它作为普通函数来对待。
#include <iostream>
using namespace std;
#define MYADD(x, y) x+y
#define MYADD1(x, y) (x+y)
#define MYMAX(a, b) (((a) > (b)) ? (a) : (b))
void test1()
{
int sum = MYADD(100, 200);
cout << sum << endl;
//宏函数本质函数宏定义,所以在预处理阶段就是直接替换,所以不会自动将结果看做一个整体
//MYADD(10, 5) * 100 --> 10 + 5 * 100
int num = MYADD(10, 5) * 100;
cout << num << endl;
num = MYADD1(10, 5) * 100;
cout << num << endl;
int max = MYMAX(100, 20);
cout << "max = " << max << endl;
int a = 10, b = 5;
//使用宏函数有时会有歧义
//MYMAX(++a, b);
// -->
// ++a > b ? ++a : b;
max = MYMAX(++a, b);
cout << "max = " << max;
}
//使用inline修饰的函数称之为内联函数
//一般只能将简短的函数作为内联函数,如果函数内容过多即使加了inline也是普通函数
//如果简短的函数没有加inline并且会经常调用,编译器会将这个函数自动当做内联函数来使用
inline int MyMax(int a, int b)
{
return a > b ? a : b;
}
void test2()
{
int a = 10, b = 5;
int max = MyMax(++a, b);
cout << "max = " << max;
}
int main()
{
test2();
return 0;
}
十 一、函数的默认参数(缺省参数)
C++里面函数的形式参数可以设置默认值,在调用的时候如果不给对应位置的形参赋值,就会自动赋值为默认值,但是设置默认值的时候必须从右向左连续赋值
#include <iostream>
using namespace std;
void Dc21071StuMsg(string strclass, int id, string name, char sex = 'M', int score = 0)
{
cout << strclass << ", ";
cout << id << ", ";
cout << name << ", ";
cout << sex << ", ";
cout << score << endl;
}
void test1()
{
Dc21071StuMsg("dc21071", 1001, "张三", 'M', 90);
Dc21071StuMsg("dc21071", 1002, "李四", 'W', 80);
Dc21071StuMsg("dc21071", 1003, "王五");
}
int main()
{
test1();
return 0;
}