科林C++_5 探索程序

一、头文件 源文件

头文件:变量的声明、函数的声明、类的定义

源文件:对应变量的敌营、函数定义、类成员属性的定义、类成员函数定义

为什么包含头文件而不是源文件?包含源文件会有重定义错误

为什么可以跨文件调用?连接器会把编译好的文件连起来

#pragma once
//AA.h
extern int a;	//声明变量

void fun();	//函数声明

class CTest;	//类的声明

class CTest {	//类的定义,注意:类的定义可以重复(在不同的源文件中),所以把类的定义放在头文件中,防止混淆
public:
	int m_a;
	static int m_b;
	const int m_c;
	int& m_d;
public:
	CTest();	//声明,也可以放定义
	~CTest();
	static void funstatic();
	void funconst()const;
	virtual void funvirtual();
};
//AA.cpp
#include <iostream>
using namespace std;

int a = 1;
void fun() {
	cout << a << endl;
}

#include "AA.h"
//类中的成员函数在类外定义,需要在函数名前+类名作用域

int CTest::m_b = 2;	//在源文件中定义及初始化,不能放在头文件中定义,会有重定义错误

CTest::CTest():m_a(1) ,m_c(3),m_d(m_a){	//常量、引用、虚函数指针、父类的构造函数 必须在构造函数的初始化列表中初始化
	cout << "CTest" << endl;
}

CTest::~CTest(){
	cout << "~CTest" << endl;
}

void CTest::funstatic(){	//静态函数static关键字要去掉,修饰整个函数
	cout << "funstatic" << endl;
}

void CTest::funconst() const{	//常函数const关键字要保留,修饰this指针类型
	cout << "funconst" << endl;
}

void CTest::funvirtual(){	//虚函数virtual关键字描述函数整体,要去掉
	cout << "funvirtual" << endl;
}
//main.cpp
#include "AA.h"
#include <iostream>

using namespace std;

int main() {
	
	fun();

	CTest t;
	cout << t.m_a << endl;
	cout << CTest::m_b << endl;
	cout << t.m_c << endl;
	cout << t.m_d << endl;
	CTest::funstatic();
	t.funconst();
	t.funvirtual();
}

头文件与源文件的差异:

头文件:单独的头文件不参与编译

源文件:每一个源文件都是自上而下独立编译

二、头文件重复包含

//A.h
//#pragma once
class A {
public:
	int m_a;
	A(int a);
};
//A.cpp
#include "A.h"

A::A(int a) :m_a(a){}
//B.h
//#pragma once
#include "A.h"
class B {
public:
	A m_a1;
	B() :m_a1(1) {}
};
//C.h
//#pragma once
#include "A.h"
class C {
public:
	A m_a2;
	C() :m_a2(2) {}
};
//main.cpp
#include <iostream>

using namespace std;

#include "B.h"
#include "C.h"

int main() {
	B b;
	cout << b.m_a1.m_a << endl;
	C c;
	cout << c.m_a2.m_a << endl;
}

ERROR:C2011 A::class 类型重定义

解决去重的方法:

1、#pragma once

告诉编译器,当前这个头文件在其他的源文件中,只需要包含一次

2、宏的逻辑判断

#pragma once

#ifndef __A_H__   //如果没有定义 __A_H__ 宏,可以往下走

#define __A_H__	//定义宏

class A {
public:
	int m_a;
	A(int a);
};

#endif // !__A_H__

对比:

1、效率不同:#gragma once 和编译器直接沟通,编译程序效率高。通过宏逻辑判断,处理大量头文件时,效率略低

2、操作难易:#gragma once 程序自带,简单。基于逻辑宏的判断,若 宏名字 重复,会导致程序问题

基于逻辑宏的判断优势: 可以做到手动局部控制部分代码是否包含

三、编译器-运行期

3.1 编译期确定 / 运行期确定

#include <iostream>

using namespace std;

int main() {
	//编译期确定
#if __cplusplus
#define A 1
#else 
#define A 2
#endif
	int a = A;

	//运行期确定
	cin >> a;
	if (a > 10)
		cout << ">10" << endl;
	else
		cout << "<10" << endl;
}

3.2 编译期错误 / 运行期错误

	//在编译期产生的错误
	int len = 10;
	//int arr[len];	//ERROR:变量len的值不可以做常量

	//在运行期产生的错误
	int* p = new int[len];
	p[len + 1] = 1;	//ERROR:数组越界

3.3 类有关的问题

char buffer[1024] = { 0 };

class A {
public:
	virtual void fun() {
		cout << "A" << endl;
	}
};

class B :public A{
private:		//访问修饰夫编译期的概念(限制作用)
	void fun() {
		cout << "B" << endl;
	}
	void fun2() {
		cout << "B::fun2" << endl;
	}
public:
	void Getfun2() {
		printf("%p\n", &B::fun2);	//输出地址到控制台,不用cout因为解析不正确
		sprintf(buffer, "%d\n", &B::fun2);
	}
};

int main() {
	A* pa = new B;
	pa->fun();	//B

	//在类外直接调用类成员函数

	B b;
	b.Getfun2();
	cout << buffer << endl;

	//to_string();
	int a = atoi(buffer);
	void(*p)() = (void(*)())a;
	(*p)();	//B::fun2

}

运行期多态,访问修饰符限制在编译期,所以可以在类外调用B(private)

原因在于在运行期时,实际调用的是地址

3.4 宏-内联函数

3.4.1 宏#define

3.4.1.1 宏的特殊用法

\ :连接当前行和下一行

注意: \ 后面不能有任何字符。最后一行一般不加 \ ,以防影响后续代码

#define A 10

#define B \
	for (int i=0;i<A;i++){\
		cout << i << " ";\
	}\
	cout<<endl;

()宏 可以带参数,参数的作用也是替换。可以等效为函数

#define C(N) \
	for (int i=0;i<N;i++){\
		cout << i << " ";\
	}\
	cout<<endl;

#define Mul(a,b) a*b

int Mull(int a, int b) {
	return a * b;
}

宏的参数没有校验的功能(类型的安全检查)

宏的参数只是单纯的替换,不会自动的计算和表达式的求解

		cout << Mul(2, 3 + 4) << endl;	//10
	cout << Mull(2, 3 + 4) << endl;		//14

# :将宏参数转化为字符串,相当于加上了“ ”

#define D(N) #N

void fun(char c) {
	cout << "fun(char)" <<c<< endl;
}
void fun(const char* c) {
	cout << "fun(char*)"<<c << endl;
}
void fun(int c) {
	cout << "fun(int)" <<c<< endl;
}

	fun(D(1));	//1
	fun(D("ABC"));	//"ABC"

#@ :将参数转化为字符,相当于加上‘ ’

##:拼接

3.4.1.2 限制宏的范围

#undef 取消宏定义

#undef A
	int A = 3;

函数名与宏重名

    G(1);	//默认使用 宏
	(G)(2);	//用括号将函数名括起来,使用的是函数

3.4.2 内联函数inline

inline  建议性关键字: 建议编译器将其当成内联函数使用,而非必须。(取决于编译器)

宏 普通函数 内联函数三者区别:

1、宏发生在预处理期,普通函数和内联函数都在编译期

2、宏和内联函数效率高、普通函数效率慢

3、内联函数和宏本质是复制粘贴,会导致占用内存大(空间换时间

注意:递归函数  虚函数  一定不能为内联函数

类、结构中在的类内部声明并定义的函数 默认为内联函数

#include <iostream>

using namespace std;

//预处理期
#define Mul(a,b) ((a)*(b))

int Mull(int a, int b) { return a * b; }

//编译期
// 短小精悍 ,代码量比较少,逻辑简单的函数适合为内联函数, 如果函数代码量比较多,大量的复制替换,导致程序占用空间增大
inline int MUL(int a, int b) { return a * b; }

int main() {
	Mull(1, 2);
	MUL(1, 2 + 3);	//1*5
}

3.5 重载运算符

函数名:operator + 需要重载的运算符

参数:与操作符的规则保持一致(双目运算符->2个参数)

顺序:与使用场景一致

返回类型:一般是有的,目的是和后续的操作符继续操作

3.5.1 一般运算符

#include <iostream>
using namespace std;

class CTest {
public:
	int m_a;
	CTest():m_a(0){}

	int operator=(/* CTest* const this , */int a) {	//类内有隐藏的this指针,第一个参数不用写
		m_a = a;
		return m_a;
	}
	int operator+(int a) {	//再类内重载操作符函数,参数顺序固定,第一个参数是隐藏的this指针,只能匹配对象
		return m_a + a;
	}
};

int main() {
	CTest t;

	t = 10;
	cout << t.m_a << endl;
	*(int*)&t = 20;    //通过指针
	cout << t.m_a << endl;
	t.operator=(30);
	cout << t.m_a << endl;
	t = t + 40;
	cout << t.m_a << endl;
}

重载操作符的含义:本质上是一个函数,告诉编译器当遇到这个符号 能够匹配 的时候,调用这个函数来完成重载操作符的功能。是对原有操作符功能的扩展、补充

	int operator++() {	//左++
		return ++m_a;
	}

	int operator++(int) {	//右++比左++多一个int参数,这个参数只是为了区分,无其他意义
		return m_a++;
	}

在类外重载

int operator+(int a, CTest& t) {
	return a + t.m_a;
}
	cout << 10 + t << endl;    //通过类外重载实现该操作
    int operator++() {	//类内左++
		    return ++m_a;
	    }
int operator++(CTest& t) {    //类外
	return ++t.m_a;
}

    //cout << ++t << endl;    //ERROR:有歧义
    t.operator++();    //类内左++
    t.operator++(0);    //类内右++

    ::operator++(t);    //全局左++

发现有歧义

 3.5.2 重载输入输出操作符

要在类外重载 

ostream& operator << (ostream& os, CTest& t) {
	os << t.m_a;
	return os;
}

istream& operator >> (istream& is, CTest& t) {
	is >> t.m_a;
	return is;
}

同一个操作符,在类内重载有隐含的this指针参数,类外需要多加一个参数(类对象的引用),
类内函数参数顺序相对来说固定,在类外重载时,顺序可调 ,在类外重载操作符,参数至少要包含一个自定义类型(类、结构体)
注意: 同一个操作符 类内 和类外 如果同时存在,可能会导致使用上的歧义
 重载操作符函数,参数不能有默认值,否则违背了其使用规则
 对于同一个操作符来说,参数的数量不同,可能会代表不同的含义
 必须在类内重载的操作符: =  [] 、->  ,()
 不能改变原有操作符的优先级 和结合性  
 不能创建新的操作符

	int operator*(int a) {	//乘法
		return m_a * a;
	}

int operator*(CTest& t) {	//间接引用
	return t.m_a;
}

注意:*作为乘号是双目运算符,*作为间接寻址运算符是单目运算符。而int* p时是作为一类型(整型指针)

3.5.3 对象类型转换

函数名: operator 要重载的类型,无参数,无返回类型(类似于构造-析构,并不是返回void)

返回的数据 要和 重载的类型保持一致

#include <iostream>

using namespace std;

class CTest {
public:
	int m_a;
	CTest() :m_a(1) {}
	operator int() {	//重载类型和返回类型得保持一致
		return m_a;
	}
	//对于 重载操作符 和重载类型,同时满足调用条件,优先匹配重载操作符
	int operator+(int a) {
		return m_a + a;
	}
};

int main() {
	CTest t;
	int a = 0;
	a = t;	//a=t.m_a
	cout << a << endl;
	cout << t + a << endl;	//重载运算符

	cout << a + t << endl;	//重载类型

	cout << t.operator int() << endl;
}

3.5.4 迭代器 

typedef struct node {
	int data;
	node* next;
	node(int data) {
		this->data = data;
		next = nullptr;
	}
}Node,*PNode;

class CIterator {
public:
	PNode m_pTemp;
	CIterator(PNode p):m_pTemp(p){}
	bool operator!=(PNode p) {
		return m_pTemp != p;
	}
	int operator *() {
		return m_pTemp->data;
	}
	PNode operator++() {
		m_pTemp = m_pTemp->next;
		return m_pTemp;
	}
	PNode operator++(int) {
		PNode tmp;
		tmp = m_pTemp;
		m_pTemp = m_pTemp->next;
		return tmp;
	}
};
class CLinkedList {
private:
	PNode header;
	PNode ender;
	int len;
public:
	//void showList() {
	//	PNode p = header;
	//	while (p) {
	//		cout << p->data << ' ';
	//		p = p->next;
	//	}
	//	cout << endl;
	//}
	void showList() {
		CIterator ite = header;
		while (ite!=nullptr) {
			cout << *ite << ' ';
			ite++;
		}
		cout << endl;
	}
};

 3.5.5 list STL链表

3.5.5.1 添加(push)、弹出(pop)、遍历
#include <iostream>
#include <list>

using namespace std;

int main() {
	list<int> lst;	//定义一个空链表
	lst.push_back(2);
	lst.push_back(3);
	lst.push_front(1);
	lst.push_front(0);

	//定义迭代器对象,指向头
	list<int>::iterator ite = lst.begin();

	while (ite != lst.end()) {	//不等于尾节点
		cout << *ite << " ";
		ite++;
	}
	cout << endl;

	lst.pop_back();
	lst.pop_front();

	ite = lst.begin();
	while (ite != lst.end()) {
		cout << *ite << " ";
		ite++;
	}
	cout << endl;
}
3.5.5.2 插入、删除
ite = ++lst.begin();
	list<int>::iterator iteinsert = lst.insert(ite, 10);
	cout << *iteinsert << endl;

	for (int v : lst) {
		cout << v << " ";
	}
	cout << endl;

	ite = lst.begin();

	//返回的是删除节点的下一节点
	list<int>::iterator iteerase = lst.erase(ite++);

	cout << *iteerase << ' ' << *ite << endl;

	for (int v : lst) {
		cout << v << " ";
	}
	cout << endl;
	
	ite = ++lst.begin();
	iteerase = lst.erase(ite++);

	if (iteerase != lst.end()) {
		cout << *iteerase << endl;
		cout << *ite << endl;
	}
	else {
		cout << "ERROR" << endl;
	}
3.5.5.3 头尾节点取值
    cout << *lst.begin() << ' ' << lst.front() << endl;	//front() 返回头节点里的值
	cout << *--lst.end() << ' ' << lst.back() << endl;	//back() 返回尾节点的值



    cout << lst.size() << endl;	//获取链表的长度,有效节点的数量

	lst.clear();	//清空链表

	cout << lst.empty() << endl;	//返回bool类型

3.6 代码片段

3.7 拷贝构造

3.7.1 转换构造

#include <iostream>

using namespace std;

class CTest {
public:
	int m_a;
	int m_b;
	//转换构造函数 ,explicit 修饰构造函数,禁止构造函数发生隐式转化
	/*explicit*/ CTest(int a) :m_a(a) {}

	CTest(int a,int b) :m_a(a),m_b(b) {}
};

int main() {
	CTest t(10);
	cout << t.m_a << endl;
	CTest t2 = 20;	//初始化,可以做隐式转换
	cout << t2.m_a << endl;

	t2 = { 2,3 };
	cout << t2.m_b << endl;

}

3.7.2 拷贝构造

#include <iostream>

using namespace std;

class CTest {
public:
	int m_a;
	int* m_p;
	CTest(int a):m_a(a),m_p(new int(++a)){}
	/*
	拷贝构造函数 参数:const + 当前类对象的引用
	函数体代码:形参对象中的成员依次给this对象中的成员做初始化
	一个对象给另一个对象做初始化
	空类中,编译器会默认提供拷贝构造函数,一旦我们手动重构他,编译器就不会提供默认的了
	 
	浅拷贝问题:多个对象的指针成员指向了同一个申请的卡空间,会导致同一个内存空间被回收了多次,使程序出现异常
	默认的拷贝构造函数,是一个浅拷贝
	深拷贝,手动重构解决浅拷贝问题。对于指针成员,需要单独申请一个属于自己的空间
	
	避免浅拷贝问题发生,
	*/
	
	CTest(const CTest &t):m_a(t.m_a),m_p(t.m_p) {	//引用传递
		if (t.m_p)
			m_p = new int(*t.m_p);
	}
	~CTest() {
		if (m_p)
			delete m_p;
		m_p = nullptr;
	}
};

void fun(CTest& t) {	//避免值传递,推荐使用引用、指针
	cout << "fun" << *t.m_p << endl;
}

int main() {
	CTest t1(1);
	CTest t2(t1);
	cout << t2.m_a<<*t2.m_p << endl;
	fun(t2);
}

3.7.3 operator=

#include <iostream>

using namespace std;

/*
空类中 默认存在的函数
1.默认无参构造
2.析构函数
3.拷贝构造函数
4.默认的operator=
*/

class CTest {
public:
	int m_a;
	int* m_p;
	explicit CTest(int a):m_a(a),m_p(new int(a)){}
	~CTest() {
		if (m_p)
			delete m_p;
		m_p = nullptr;
	}
	//int const* operator=(int* const p)[]
	/*
	默认提供的函数 函数名:operator= 参数:const 当前类对象的引用,返回类型:当前类对象的引用
	函数体代码:形参对象中 依次 给this中的成员做初始化
	默认的operator=是个浅拷贝


	*/
	
	//CTest& operator=(const CTest& t) {
	//	if (this!=&t) {	//如果不是自己,则赋值
	//		this->m_a=t.m_a;
	//		if (t.m_p) {
	//			this->m_p ? *this->m_p = *t.m_p : (this->m_p = new int(*t.m_p), 0);
	//		}
	//		else {
	//			if (this->m_p)
	//				delete this->m_p;
	//			this->m_p = nullptr;
	//		}
	//	}
	//	return *this;
	//}
};

int main() {
	/*
	CTest t1(1);
	int a = 0;

	//t1=&a;	//赋值,有对应匹配的函数

	CTest t2(2);

	t1 = t2;

	cout << t2.m_a << endl;

	cout << *t2.m_p << endl;

	t1 = t1;
	*/

	CTest t(1);
	t = CTest(2);
	t = 3;	//禁止隐式转换后,该写法不支持
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
neokylin-live-desktop-7.0-x86_64-b052-20170727是新科林操作系统的一个版本。新科林是中国自主开发的一个Linux操作系统,旨在为中国市场提供高度定制化的解决方案。该版本为x86_64架构,并于2017年7月27日发布。 新科林操作系统以其稳定性、安全性和易用性而闻名。它基于Linux内核,并加入了许多国内外技术专利,以便更好地满足中国用户的需求。该操作系统支持多种语言,并提供大量的本土化软件和应用程序,以便用户根据自己的喜好和需求进行定制。 neokylin-live-desktop-7.0-x86_64-b052-20170727是一个可以直接从USB或光盘启动的镜像文件,用户可以通过这个镜像文件以“live”模式运行新科林操作系统,而无需将其安装到计算机硬盘上。这种方式对于用户来说非常方便,因为他们可以在不更改原有操作系统的情况下尝试新科林,并决定是否要安装它。 这个版本提供了一个友好的图形用户界面,使用户可以轻松地浏览、访问和管理他们的文件和应用程序。同时,新科林操作系统也支持多媒体功能和网络连接,用户可以在系统中轻松播放音乐、观看视频,并与互联网进行交互。 总之,neokylin-live-desktop-7.0-x86_64-b052-20170727是新科林操作系统的一个特定版本,通过这个版本,用户可以方便地尝试新科林操作系统,并决定是否安装它以替代之前的操作系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值