C++对C语言的加强
1.命名空间(namespace)
为了避免,在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发送冲突。
标准C++引入了关键字namespace,可以更好地控制标识符的作用域。
std是C++标准命名空间,C++标准程序库中的所有标识符都被定义在std中,比如标准库中的类iostream、vector等都定义在该命名空间中,使用时要加上using声明(using namespace std)
C++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h
#include <iostream>using namespace std;int main()
{int a = 0;cout << "Hello World!" << endl;cin >> a;cout << "a= " << a << endl;return 0;
}
<<和>>表示数据的流向,cout表示屏幕,cin表示键盘输入
引用命名空间的三种方式
1.std::cout std::endl
2.using std::cout using std::cin
3.using namespace(该空间下的都可使用,上述两个是仅使用几个标识符)
命名空间的定义
namespace spaceA{int g_a = 11;
}int main()
{cout << spaceA::g_a << endl;return 0;
}
当命名空间嵌套时,要一直引用到最内层命名空间
namespace spaceB {int a;namespace spaceC{struct teacher{int id;char name[10];};}namespace spaceD{struct teacher{int id;char name[10];};}
}int main()
{using spaceB::spaceC::teacher;teacher t1;t1.id = 10;cout << t1.id << endl;return 0;
}
还有一种不常用的形式
namespace spaceB {int a;namespace spaceC{struct teacher{int id;char name[10];};}namespace spaceD{struct teacher{int id;char name[10];};}using namespace spaceC;
}int main()
{using spaceB::teacher;teacher t1;t1.id = 10;cout << t1.id << endl;return 0;
}
2.对C的增强和bool类型
对全局变量的定义检测性增强
int g_val;
int g_val = 20;
该段代码在C语言中不会报错,但在C++中会重定义报错
报错的意义是int g_val和int g_val = 20分配的内存位置是不同的,int g_val分配的是内存的BSS段,BSS段用于存储未初始化的全局变量和静态变量。它的主要目的是节省存储空间,因为未初始化的变量在程序加载时不会占用实际的存储空间,只在运行时分配内存。int g_val = 20分配的是内存的data段,data段用于存放已初始化的全局变量和静态变量。它与BSS段不同,因为.data段在文件中占用实际的存储空间。以及搞驱动时要清楚变量放在内存的哪个区。
struct类型增强
struct student
{char name[10];int id;
};
声明完上述结构体类型后,C语言定义结构体类型需要struct student s1(除非使用typedef),C++定义结构体类型只需student s1(这里是把结构体当成一个类来处理了)
C++中所有变量和函数都必须有类型
C语言中的默认类型在C++中是不合法的
接收参数的个数错了直接报错,C语言是报警告
新增bool类型关键字
bool flag = true;
flag = false;
true ->1 false -> 0
非0即是1
sizeof(bool)的值为1
三目运算符的增强
int a = 10;
int b = 20;
(a<b)?a:b = 50;
上述代码,C语言会报错,C++会把a变成50,因为C语言返回的是值10,C++返回的是变量a
const的增强
const基础知识
int main(void)
{//const 定义常量--->const 意味只读const int a;int const b;//第一个第二个意思一样 代表一个常整形数const int *c;//第三个 c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)int * const d;//第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)const int * const e ;//第五个 e一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)return 0;
}
const int a = 10;
int *p = &a;
*p = 20;
上述代码C语言中a会被修改为20,而在C++中却不会,因为const常量是在text段的符号表中的一个键值对,其没有地址,而&a会创建一个临时空间让指针p指向该临时空间,对*p赋值也只是对折这段临时空间赋值
枚举的增强
c语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而 c++中枚举变量,只能用被枚举出来的元素初始化。
3.引用
(1)基本概念
引用可以理解为一个变量的别名
引用数据类型:int &
int a = 10;
int &re = a;
re = 20;
a的值就变成了20
引用的四个规则
1 .引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
2 .声明的时候必须初始化,一经声明,不可变更
3 . 可对引用,再次引用。多次引用的结果,是某一变量具有多个别名
4 . &符号前有数据类型时,是引用。其它皆为取地址
引用的应用
引用可以代替指针的一些简单使用
void change_value(int & r)
{r = 20
}int main()
{int a = 1;change_value(a);
}
a的值就改变为20了
(2)引用的本质
引用所占用的大小跟指针是相等的,引用的本质是一个常量指针
(3)引用作为函数的返回值
引用作为返回值,不要返回局部变量的引用
引用如果当函数返回值,函数可以当左值
int & A()
{static int a = 20;return a;
}int main()
{A() = 50;
}
a 的值就被修改为50了
(4)指针引用
struct teacher
{int id;char name[64];
};int get_mem(struct teacher * &tr)
{tr = (struct teacher *)malloc(sizeof(struct teacher));if (tr == NULL){return -1;}tp->id = 100;strcpy(tp->name,"zhang3");return 0;
}void free_mem(struct teacher * &tr)
{if (tr != NULL){free(tr);tr = NULL;}}
(5)const引用
如果想对一个常量进行引用,必须是一个const引用
对一个变量进行引用,可以是const引用,但这仅仅是不能通过引用来修改该变量,该变量还是可以变的
4.内联函数
特点
1)内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求。
2)C++编译器直接将函数体插入在函数调用的地方
3)内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)。
4)内联函数是一种特殊的函数,具有普通函数的特征(参数检查,返回类型等)
5)内联函数 由编译器处理,直接将编译后的函数体插入调用的地方宏代码片段
宏定义由 由预处理器处理,进行简单的文本替换,没有任何编译过程。
6)C++中内联编译的限制:
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数进行取址操作
函数内联声明必须在调用语句之前
7)编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。
适用场景:函数体很小,且被频繁使用的场景
5.函数的默认参数和占位参数
默认参数值要从右往左写
占位参数:
void fun(int x, int)
{cout << "a = " << a << endl;
}int main()
{fun(10,20); //这里必须传递两个参数return 0;
}void fun(int x, int=0)
{cout << "a = " << a << endl;
}int main()
{fun(10,20); //这里必须传递两个参数fun(25); //这里可传一个可传两个return 0;
}
6.函数重载和函数指针
函数名相同,参数列表不同即为函数重载,返回值与函数重载无关
函数重载避免使用默认参数,避免产生函数歧义
根据调用时的参数列表来确定使用哪个函数
优先调用完全匹配的
没有完全匹配的,会隐式转换
都转换不了的,匹配失败
函数指针只指向一个重载函数,且只能指向参数匹配的一项
实际上在给函数指针赋值的时候,是会发生函数重载匹配的
在调用函数指针的时候,所调用的函数就已经固定了。
函数指针写法:
int fun(int a, int b)
{cout << "fun1" << endl;return 0;
}int main()
{int(*fp)(int, int) = NULL;fp = fun;fp(1,2);
}
7.类
(1)类的基本概念
class SuperHero
{
public:char name[64];int sex;void printHero(){cout << "name = " << name << endl;cout << "sex = " << sex << endl;}
};int main()
{ SuperHero Sp;strcpy(Sp.name, "Spiderman");Sp.sex = 1;Sp.printHero();return 0;
}
(2)类的封装特性
在public定义下的成员变量和成员方法。可以在类的内部和外部访问
在private定义下的成员变量和成员方法,只可以在类的内部访问
protected下的,在单个类中与private相同,在继承中不同
封装实现对外隐藏数据,对外提供接口
struct默认的访问控制权限是public,class默认的访问控制权限是private
(3)面向对象编程实例
(圆的周长和面积)
Circle类包含两个文件:Circle.h和Circle.cpp
Circle.h
#pragma onceclass Circle
{
public:void setR(double r);double getR();double getArea();double getGirth();
private:double m_r;double m_area;double m_girth;
};
Circle.cpp
#include "Circle.h"void Circle::setR(double r)
{m_r = r;
}double Circle::getR()
{return m_r;
}double Circle::getArea()
{m_area = 3.14 * m_r * m_r;return m_area;
}double Circle::getGirth()
{m_girth = 3.14 * m_r * 2;return m_girth;
}
main.cpp
#include <iostream>#include "Circle.h"
using namespace std;
int main()
{Circle c;c.setR(5);cout << c.getR() << endl;cout << c.getArea() << endl;cout << c.getGirth() << endl;return 0;
}
(比较两立方体是否相等)
#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;}double getArea(){return (m_a * m_b) * 2 + (m_a * m_c) * 2 + (m_c * m_b) * 2;}double getVolume(){return m_a * m_b * m_c;}//同类之间无私处bool judgeCube(Cube another){if (m_a == another.m_a && m_b == another.m_b&& m_c == another.m_c){return true;}else{return false;}}
private:double m_a;double m_b;double m_c;
};int main()
{Cube c1, c2;c1.setABC(10, 20, 30);c2.setABC(10, 20, 30);if (c1.judgeCube(c2)){cout << "相等" << endl;}else{cout << "不相等" << endl;}return 0;
}
(判断点是否在圆内)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;class Point
{
public:void setXY(double x, double y){m_x = x;m_y = y;}double getX(){return m_x;}double getY(){return m_y;}
private:double m_x;double m_y;
};class AdvCircle
{
public:void setXYR(double x, double y, double r){m_x = x;m_y = y;m_r = r;}double getX(){return m_x;}double getR(){return m_r;}double getY(){return m_y;}
private:double m_r;double m_x;double m_y;
};void judgeposition(AdvCircle & c, Point & p)
{if (((p.getX() - c.getX()) * (p.getX() - c.getX()) + (p.getY() - c.getY()) * (p.getY() - c.getY())) > (c.getR()) * c.getR()){cout << "点在圆外" << endl;}else if (((p.getX() - c.getX()) * (p.getX() - c.getX()) + (p.getY() - c.getY()) * (p.getY() - c.getY())) < (c.getR()) * c.getR()){cout << "点在圆内" << endl;}else{cout << "点在圆上" << endl;}
}int main()
{AdvCircle c1;Point p1;c1.setXYR(1, 1, 2);p1.setXY(0, 0);judgeposition(c1, p1);
}
也可把方法写入点中或圆中均可,也可多文件编写
(4)
作业1:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <math.h>
class Point
{
public:void setXY(double x, double y){m_x = x;m_y = y;}double getX(){return m_x;}double getY(){return m_y;}double distance(Point& another){double d = sqrt((m_x - another.m_x) * (m_x - another.m_x) + (m_y - another.m_y) * (m_y - another.m_y));return d;}
private:double m_x;double m_y;
};class Circle
{
public:void setXY(double x, double y){m_p.setXY(x, y);}void setR(double r){m_r = r;}double getX(){return m_p.getX();}double getY(){return m_p.getY();}double getR(){return m_r;}bool isIntersect(Circle another){double Inter = m_p.distance(another.m_p);if ( Inter >= abs(m_r-another.m_r) && Inter <= m_r + another.m_r){return true;}else{return false;}}
private:Point m_p;double m_r;
};int main()
{Circle c1, c2;double x, y, r;cout << "请输入第一个圆的圆心:" << endl;cin >> x;cin >> y;cout << "请输入第一个圆的半径:" << endl;cin >> r;c1.setXY(x, y);c1.setR(r);cout << "请输入第二个圆的圆心:" << endl;cin >> x;cin >> y;cout << "请输入第二个圆的半径:" << endl;cin >> r;c2.setXY(x, y);c2.setR(r);if (c1.isIntersect(c2)){cout << "两圆相交" << endl;}else{cout << "两圆不相交" << endl;}return 0;}
作业2:
class Rectangle
{
public:void setLeft(double x, double y){Left_lower.setXY(x, y);}void setRight(double x, double y){Right_upper.setXY(x, y);}double getArea(){double Area = (Right_upper.getX() - Left_lower.getX()) * (Right_upper.getY() - Left_lower.getY());return Area;}
private:Point Left_lower;Point Right_upper;
};int main()
{Rectangle R1;R1.setLeft(1, 1);R1.setRight(4, 3);cout << R1.getArea() << endl;return 0;}
作业3:
class Tree
{
public:void setAges(int ages){m_ages = ages;}void grow(int years){m_ages = m_ages += years;}void age(){cout << m_ages << endl;}
private:int m_ages = 0;
};int main()
{Tree t1;t1.age();t1.setAges(12);t1.age();t1.grow(7);t1.age();return 0;}
8.对象的构造和析构
(1)构造
构造函数是C++给类提供的一个给对象的初始化方案
类中可以定义与类名同名的成员函数,这就是构造函数
class Test
{
public:Test(){m_x = 0;m_y = 0;}Test(int x){m_x = x;m_y = 0;}Test(int x, int y){m_x = x;m_y = y;}
private:int m_x;int m_y;
};int main()
{Test t1; //这里用的是Test()构造函数Test t1(10); //这里用的是Test(int x)构造函数Test t1(10, 20); 这里用的是Test(int x, int y)构造函数return 0;
}
构造函数是可以重载的,无参的就是默认构造函数
(2)析构
析构函数是在类对象销毁时操作系统自动调用的,析构函数没有形参,析构函数不能重载
class Test
{
public:Test(){m_x = 0;m_y = 0;name = char(*)malloc(100);}Test(int x){m_x = x;m_y = 0;}Test(int x, int y){m_x = x;m_y = y;}~Test(){if (name != NULL){free(name);cout << "free succ" << endl; }}
private:int m_x;int m_y;char *name;
};void test()
{Test t1;
} //t1被销毁时就会调用~Test();int main()
{test();
}
(3)拷贝构造函数
class Test
{
public:Test(){m_x = 0;m_y = 0;}Test(int x){m_x = x;m_y = 0;}Test(int x, int y){m_x = x;m_y = y;}void printT(){cout << "x = " << m_x << " , y = " << m_y << endl;}Test(const Test& another){cout << "Test拷贝" << endl;m_x = another.m_x;m_y = another.m_y;}private:int m_x;int m_y;
};int main()
{Test t1(10, 20);Test t2(t1);t2.printT();return 0;
}
如果不写拷贝构造函数,默认拷贝构造函数就是将变量一一赋值
注意构造函数都是初始化时,创建类对象时调用的,如果是如下写法:
Test t1(13,25);
Test t2;
t2 = t1;
这里t2=t1调用的不是拷贝构造函数,而是赋值操作符函数operator=
(4)深拷贝和浅拷贝
使用默认拷贝函数是浅拷贝,t2和t1的m_name指向同一内存空间,析构时会free两次,导致系统崩溃
class Teacher
{
public:Teacher(int id,const char *name){m_id = id;int len = strlen(name);m_name = (char*)malloc(len + 1);strcpy(m_name, name);}void printT(){cout << "id = " << m_id << ", name = " << m_name;}~Teacher(){if (m_name != NULL){free(m_name);m_name = NULL;}}
private:int m_id;char* m_name;
};int main()
{Teacher t1(1, "zhang3");Teacher t2(t1);t1.printT();return 0;
}
有指针就要自行进行深拷贝
using namespace std;class Teacher
{
public:Teacher(int id,const char *name){m_id = id;int len = strlen(name);m_name = (char*)malloc(len + 1);strcpy(m_name, name);}void printT(){cout << "id = " << m_id << ", name = " << m_name;}Teacher(const Teacher& another){m_id = another.m_id;int len = strlen(another.m_name);m_name = (char*)malloc(len + 1);strcpy(m_name, another.m_name);}~Teacher(){if (m_name != NULL){free(m_name);m_name = NULL;}}
private:int m_id;char* m_name;
};int main()
{Teacher t1(1, "zhang3");Teacher t2(t1);t1.printT();return 0;
}
(5)构造函数的初始化列表
class ABC
{
public:ABC(int a, int b, int c){m_a = a;m_b = b;m_c = c;}~ABC(){cout << "~ABC()" << endl;}
private:int m_a;int m_b;int m_c;
};class ABCDE
{
public:ABCDE(int a, int b, int c, int d, int e) :m_abc(a, b, c), m_e(e) //调用有参构造{m_d = d;} ABCDE(ABC& abc, int d, int e) :m_abc(abc), m_e(e) //调用拷贝构造{m_d = d;}
private:ABC m_abc;int m_d;const int m_e;
};int main()
{ABC abc(10, 20, 30);ABCDE abcde1(11, 22, 33, 44, 55);ABCDE abcde2(abc, 66, 77);return 0;
}
9.对象动态建立和释放new和delete
用来代替malloc和free的
new和delete是运算符不是函数,所以运行效率高
语法:
int *array_p = new int[10];
delete[] array_p;int *p = new int;
delete p;
区别:
class Test
{
public:Test(int a, int b){m_a = a;m_b = b;}
private:int m_a;int m_b;
};int main()
{Test *tp = new Test(10,20);if ( tp != NULL ){delete tp;tp = NULL;}return 0;
}
new可以调用构造方法来初始化,而malloc不行
delete可以自动调用类对象的析构函数,而free不会调用
10.static修饰的成员变量和成员函数
1,static 成员变量实现了同类对象间信息共享。
2,static 成员类外存储,求类大小,幷不包含在內。
3,static 成员是命名空间属于类的全局变量,存储在 data 区。
4,static 成员只能类外初始化。
5,可以通过类名访问(无对象生成时亦可),也可以通过对象访问。
应用:
class Student
{
public:Student(int id, double score){m_id = id;m_score = score;m_count++;sum_score += m_score;}static int getCount(){return m_count;}static double getAver(){return sum_score / m_count;}~Student(){m_count--;sum_score -= m_score;}
private:int m_id;double m_score;static int m_count;static double sum_score;
};int Student::m_count = 0;
double Student::sum_score = 0.0;int main()
{Student a1(1, 85);Student a2(2, 86);Student a3(3, 92);Student a4(4, 73);Student a5(5, 55);cout << "学生人数为:" << Student::getCount() << endl;cout << "平均分为:" << Student::getAver() << endl;
}