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 once
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
#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;
}