C++学习

发布于:2025-02-17 ⋅ 阅读:(100) ⋅ 点赞:(0)

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

用来代替mallocfree

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


网站公告

今日签到

点亮在社区的每一天
去签到