c++基础


前言


c++文章连载:
1.C++基础
  1.C++基础
  2.C++新增和有变化的关键字
  3.C++的内存管理
2.面向对象
  1.C++的封装和访问权限
  2.C++继承和多态特性
  3.C++的运算符重载
  4.C++静态类和静态成员
  5.C++的友元函数和友元类
3.模板编程和STL
  1.C++模板编程入门
  2.STL的容器类和迭代器
  3.STL的泛型算法
  4.模板特化与类型萃取
  5.STL的其他容器讲解
  6.智能指针与STL查漏补缺
4.杂项
  1.c++各种流操作
  2.依赖,关联,聚合,组合,继承
  3.一些技巧性的代码设计
  
  


1.何为命名空间

1.1、命名空间的引入
(1)命名空间namespace,是C++引入的一种解决全局变量和函数名冲突的机制
(2)namespace的关键点有2个:一是如何解决名称冲突,二是如何合法访问变量
1.2、C语言如何解决名称冲突
(1)大项目中会有很多C文件,全局变量和函数都是extern链接属性,名称冲突客观存在。
(2)一个项目是一个单体程序,项目中的全局变量和函数理应能互相访问,因此名称冲突是客观存在
(3)C中解决名称冲突的办法是:第一,同一个C文件不要太大,由一个人写;第二,每个C文件(或几个C文件构成的一个模块)中所有全局变量和函数前加统一的唯一前缀;第三,不需要文件外访问的全局变量和函数前面都加static
1.3、命名空间如何解决问题
(1)为实现命名空间机制,C++引入了namespace关键字,定义格式为namespace xx{};
(2)一个特定名称的namespace的一对大括号内部定义的变量、函数、类等均属于该命名空间内
(3)在命名空间内部互相引用时可以直接使用变量名、函数名等
(4)跨命名空间互相引用时必须同时指定被引用方的命名空间名和变量名函数名才可以找到
(5)命名空间本质上其实是对全局变量和函数在一定范围内链接属性的更改和控制

注意:namespace下也要注意函数的声明,在调用此函数时要保证在之前就被定义过或者声明过,但是不在namespace中声明就不需要注意前后位置,如下:

namespace N1
{
	//fun3被fun1调用,则fun3要出现在fun1之前
	void fun3(void){}
	void fun1(void)
	{
		fun3();		//或者N1::fun3();
	}	
};

//也可以在N1这个命名空间之外定义函数,这样就不需要遵循先后顺序了,因为编译器通过
//N1知道了N1中所有的方法,而现在还没有去实现方法,所以方法名称在编译器中有了个记录
namespace N1
{
	void fun3(void);
	void fun1(void);
};

void N1::fun3(void)
{
}
void N1::fun1(void)
{
	fun3();		//或者N1::fun3();
}

2.namespace的初级定义和使用

2.1、同一文件内定义namespace
2.2、同一文件内使用namespace
(1)namespace的三种引用方法

// 方式一
ace::Mutex mutex;
// 方式二
using ace::Mutex;
Mutex mutex;
// 方式三
using namespace ace;
Mutex mutex;

具象的:

//第一种:
int main(void)
{
	fun1();
	return 0;
}
//第二种:
int main(void)
{
	using N1::fun1;		//fun1不带括号
	fun1();
	return 0;
}
//第三种:
int main(void)
{
	N1::fun1();
	return 0;
}

2.2、不同C++文件间定义和使用namespace

namespace NS2{void func3(void);};				//方法一
namespace NS2{extern void func3(void);};		//方法二

2.3、默认命名空间:全局命名空间

namespace NS1
{
	void func2(void)
	{
		::func5();			// 表示func5定义和声明在默认命名空间中
		func5();			// 在其他命名空间中引用默认命名空间时可以省略::
	}
};

3.C++匿名命名空间

(1)定义

namespace 
{
	void func2(void){};		//或者void func2(void){}(去掉;)
}

(2)匿名命名空间中引用其他命名空间中的方法
(3)匿名命名空间中引用自己命名空间中的方法
(4)其他命名空间中引用匿名命名空间中的方法
匿名命名空间的价值:
(1)匿名命名空间中的符号纯文件内部使用,(也就是和static相似)不需要被外部引用
(2)匿名命名空间效果类似于全局变量和函数加static,但是比C中的static使用范围广
(3)匿名命名空间的用法逻辑上符合整个命名空间的一贯原则

4.嵌套命名空间

(1)定义
(2)外部引用嵌套命名空间内的符号
(3)嵌套命名空间内部不同层次间符号的互相引用

5.C++标准库介绍

5.1
(2)C程序可以通过__cplusplus符号是否预定义来判断当前是gcc还是g++编译
__cplusplus的值是long int类型的,值表示当前编译程序的C++编译器的版本号。

(3)一个典型C++程序(后缀名.cpp)只能当C++程序来编译,可见C++是C的超集
(4)C++文件名的常用后缀:源文件(.cpp .cxx .cc .c .c++),头文件(.hpp .hxx .h)
5.2、C++完全接收并兼容了C库
(1)典型C++程序中可以支持C的形式包含C库头文件,并直接使用C库API
(2)C++更建议的头文件包含形式不是<stdio.h>这样,而是< cstdio >这样
(3)ubuntu中gcc工具链的头文件在**/usr/include**中,可以实际看看
5.3、C++标准库介绍
(1)C标准库即为C++标准库的一部分,完全继承并以C++方式重写,位于std命名空间中
(2)C++面向对象库,如string、iostream等,位于std命名空间中
(3)C++ STL标准模板库,如vector、map等,位于std命名空间中

6.iostream的cout使用

6.1、基本使用
(1)cout即标准输出,对应stdout
(2)cout定义在std命名空间中,要按三种使用方法来用
(3)结合<<符号进行输出,可多节连接
(4)cout涉及的头文件有<iostream> <iomanip> <bits/ios_base.h>
(5)cout本质上是ostream(iostream的派生类)的一个对象
(6)流操作符<<本质上是左移运算符在iostream中的运算符重载
6.2、流操作符的格式化细节
(1)网络搜索C++ cout使用,大把文档详细讲
(2)cout现在主要用来输出调试信息,掌握主要用法和细节查询即可,不必去记。

6.3、iostream的cin使用
(1)输入的时候不要使用引用符& scanf("%d", &val);
(2)cin的输入会以空格为中断

7.C++用fstream读写文件

7.1、fstream介绍,头文件包含:<fstream>
(1)fstream是C++标准库中面向对象库的一个,用于操作流式文件
读/写可用read/write函数,也可以用流操作符。
(2)fstream本质上是一个class,提供file操作的一众方法,
用法:用fstream类来定义一个对象,此对象就会包含各种fstream支持的方法:
fstream fs;

方法有:fs.open(“1.txt”); fs.is_open(); fs.write(要写的数据,数据大小);
fs.read(要读的数据,数据大小); fs.close();
c语言也有类似的库,Linux API也提供了文件读写的方法(见文章:Linux文件IO),但是还是建议使用c或者c++提供的库,因为库在API的基础上提供了扩展功能比如支持行缓存和无缓存(缓存 见文章:不同缓存方法在进程中的表现以及效率(Linux))。
7.2、C++标准库查询参考
(1)man手册:https://blog.csdn.net/u012675539/article/details/50257343
(2)http://www.cplusplus.com/reference/
(3)https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5
7.3、fstream使用举例

#include <fstream>
#include <iostream>
using namespace std;
int main(void)
{
	fstream fs;		// 定义了一个fs对象用来做后续操作
	char str[] = "i love c++";
	char rstr[16] = {0};
	fs.open("1.txt");
	if (fs.is_open())
		cout << "open success" << endl;
	else
		cout << "open fail" << endl;
	// 文件写入
	//fs.write(str, sizeof(str));
//	fs << str << endl;	
	// 文件读出
//	fs.read(rstr, sizeof(rstr));
	fs >> rstr;	
	cout << "read content: " << rstr << endl;
	fs.close();	
	return 0;
}

8.C++字符串string类初步使用

8.1、C++式字符串的使用

#include <iostream>
#include <string>
using namespace std;
int main(void)
{
	string s1, s2, s3;	
	s1 = "acbcttt";
	s2 = "def";
	s3 = s1 + s2;		// s1 += s2; 这样做也可以
/*
	cout << "the length of s1 = " << s1.size() << endl;
	cout << "the capatity of s1 = " << s1.capacity() << endl;
*/	cout << "s1.append(s2) = " << s1.append(s2) << endl;
	cout << "s3 = " << s3 << endl;
	//s1.swap(s2);
	//cout << "s1 = " << s1 << "  " << "s2 = " << s2 << endl;
	return 0;
}

12.2、C++字符串和C字符串的对比
(1)C语言严格说没有字符串的概念,C字符串其实就是字符数组或字符指针
(2)C++和之后的java等都有字符串,本质是一个class
(3)C++字符串的优势是标准库自带可用于字符串的各种处理算法和方法
(4)C++实际开发中建议使用C++字符串而不是沿用C式字符串

之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为他和前者比较起来,不必担心内存是否足够、字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下(甚至是100%)的需要。我们可以用 = 进行赋值操作,== 进行比较,+ 做串联。我们尽可以把它看成是C++的基本数据类型。必须包含头文件 。

9.C与C++混合编程

9.1、概况
(1)程序编译过程:源文件->目标(库)文件->可执行程序->镜像文件
(2)混合编程的“混合”操作发生在链接这一步
9.2、C++和C混合编程的困难所在
(1)C++和C都是编译型语言,互相混合相对容易
(2)难点:C++支持函数名重载,而C不支持,因此编译器生成目标文件时,函数名在目标文件中的临时内部名称规则不同。导致链接时符号对不上
(3)解决方案:使用extern “C”{}; 让C++在对接的局部向C妥协兼容:
#ifdef _cplusplus
extern “C”
{
#endif
//
//内容
//
#ifdef _cplusplus
}
#endif
9.3、使用objdump工具来研究函数编译后的符号
(1)写个典型的C语言库mylib.c和mylib.h,提供add和sub等几个函数
(2)使用gcc -c -o编译得到库文件,再objdump -d反汇编得到.i文件
(3)对比加不加extern "C"这2种情况下得到的.i文件的符号差异
实验第1步:证明了C语言中名称为add的函数,编译后符号表中就叫add
实验第2步:证明了C++语言中名称为add的函数,编译后符号表中叫_Z3addii
在c++编译规则中,会对函数名进行加工,而c不会,加extern在编译时不对函数名加工。
分析:同样的源码,编译后生成的二进制代码其实是一样的,所以功能其实也是一样的
所以本质上是可以混合编程的,但是生成的中间符号名称不同,所以链接器难受
实验第3步:证明了在C++的头文件中,只要把C++的函数的声明放在extern “C”{}的大括号范围之内,就可以让g++在编译这个函数时生成中间符号名时按照C的规则而不是按照C++的规则,所以这样的函数就可以和C的库进行共同链接。

g++ -E main.cpp -o main.i
9.4、C与C++混合编程的可能情况分析
(1)同一个项目全部有源码,一次编译链接。
(2)同一个项目中C是库,C++是源码,C++调用C
制作库命令:gcc -c clib.c -o clib.o
ar -r libclib.a clib.o
链接命令:g++ main.cpp -lclib -L.

(3)同一个项目中C++是库,C是源码,C调用C++
9.5、第一种情况
(1)可能性1:全部使用g++编译。不推荐
(2)可能性2:在C的头文件中加extern "C"声明
9.5、第二种情况
(1)这种是最典型的常见情况
(2)通用解决方案:在C的头文件中加extern "C"声明,在C++中直接包含头文件调用即可

10.C调用C++库的方法
10.1、C调用C++的麻烦
(1)g++和gcc的编译时符号差异
(2)c++支持很多c并不支持的特性,如函数重载
(3)解决方案:添加一层封装层
10.2、代码实战:C调用C++库中的函数
(1)用cpp写一个库,mylib.cpp mylib.hpp,用g++编译成静态库
(2)objdump反编译库,查看确认符号
(3)用cpp写一个封装层,用上extern “C”,cmylib.cpp和cmylib.hpp,用g++编译成静态库
(4)objdump反编译库,查看确认符号
(5)用c写一个main.c,调用wrapper库,用gcc编译链接,运行查看结果
gcc cppadd.cpp -c -o cppadd.o
ar -r libcppadd.a cppadd.o
g++ cppaddwrapper.cpp -c -o cppaddwrapper.o
ar -r libcppaddwrapper.a cppaddwrapper.o
gcc test.c -lcppaddwrapper -lcppadd -L. //这依赖两个库

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值