c++-基础
0. 概念
命名空间
namespace
namespace
是c++中的关键字,用来定义一个命名空间
可以包含:变量、函数、类、typedef、#define
使用的时候,name::var
name::func
…
namespace name{
// var
// func
// classes
}
namespace name{
int var=1;
void func(){cout<<"string"<<endl;>};
}
命名空间只能定义在全局范围内,不能定义在函数内
命名空间可以嵌套命名空间,name1::name2::var
命名空间内的成员可以进行声明和定义的分开
namespace name{
void func(int a);
}
void name::func(int a)
{
/*code*/
}
using
使用
using
声明了指定命名空间的成员,表示:后面的程序如果出现未指明命名空间的成员,则使用using语句声明的成员
using
不仅可以针对某个命名空间中的定义,还可以声明整个命名空间,则当程序中出现未指明命名空间的成员时,利用using声明下的成员
namespace name1{ int var;}
namespace name2{ int var;}
using name1::var;
var=1;
name2::var=2;
// 声明整个命名空间
using namespace name
命名空间与头文件
不带
.h
的头文件为C++新标准中的,所有的符号都位于命名空间std
中,使用时需要声明命名空间std
带.h
的头文件,没有使用任何命名空间,所有符号都位于全局作用域
#include <cstdlib>
#define SIZE 10
int *p=(int *)malloc(SIZE*sizeof(int);
- 推荐写法
在函数内部使用std,防止全局命名空间的冲突
int main()
{
using namespace std;
}
域操作符 ::
用来指定成员的作用域
区分局部变量和全局变量
int a=2;
int main()
{
int a=10;
cout<<a<<endl; // 10
cout<<::a<<endl; // 2
}
// 上面等价于
namespace {
int a=2;
}
int main()
{
int a=10;
cout<<a<<endl; // 10
cout<<::a<<endl; // 2
}
命名冲突问题
注意:使用using声明或using编译指令会增加命名冲突的可能性。也就是说,如果有名称空间,并在代码中使用作用域解析运算符,则不会出现二义性
// 例子1
namespace A{
int a=2;
}
int main()
{
using A::a
// 下面是错误的 using 指定的成员 与 就近原则 不能同时出现
// int a=10; // 命名冲突
cout<<a<<endl; // error
// cout<<::a<<endl; // error
}
// 例子2
namespace A{
int a=2;
}
int main()
{
using namespace A; // 对于using整个命名空间,与就近原则不冲突
int a=10;
cout<<a<<endl; // 10
cout<<A::a<<endl; // 2
// cout<<::a<<endl; // error
}
命名空间与多文件
另外,不同头文件中也可以使用相同名字的命名空间,但是前提是位于该命名空间中的成员必须保证互不相同,这样可以构成重载,见P496
/* s1.h */
#ifndef _STUDENT2_H
#define _STUDENT2_H
#include <iostream>
namespace S1
{
void display();
int num=20;
}
void S1::display()
{
std::cout<<"S1:display"<<std::endl;
}
#endif
/* s2.h */
#ifndef _STUDENT3_H
#define _STUDENT3_H
#include <iostream>
namespace S2
{
void display(int a);
}
void S2::display(int a)
{
std::cout<<"S1:display"<<std::endl;
}
#endif
/* s_main.cpp */
#include "student2.h"
#include "student3.h"
using namespace std;
int main()
{
S1::display();
cout<<S1::num<<endl;
S2::display(10);
return 0;
}
c/c++ 混合编程 --还没看
https://blog.csdn.net/fuhanghang/article/details/114831993
动态内存分配
手动开辟和释放
malloc/free
int *p=(int *)malloc(sizeof(int));
free(p);
new/delete
new
在堆上创建的对象是匿名的,无法直接使用,必须使用一个指针指向它
https://blog.csdn.net/baidu_31437863/article/details/86558339
int *p=new int;
delete p;
int *p=new int[10];
delete[] p;
malloc/new 的不同 – 还没看完
用
new
分配内存时会调用构造函数,用delete
释放内存时会调用析构函数。
new
在堆上创建的对象是匿名的,无法直接使用,必须使用一个指针指向它
new 二维数组 – 还没看
关键字
const
c语言const 见 “c语言-基础”
c语言const 全局变量 可以使用extern 使得工程可见
c++ const 全局变量 不能使用extern , 只能是当前文件可见,须使用一个指针指向它
const头文件 – 多文件
推荐使用
c++中,全局的const对象默认是没有extern声明的,所以只在当前文件中有效(不同于c语言),把这样的对象写进头文件中,即使被包含到其他多个cpp文件中,这个对象也都只在包含它的那个文件中有效,对其他文件来说是不可见的,所以不对导致多重定义(extern const 就会出现重复定义的问题)
/* demo.h */
#ifndef _DEMO_H
#define _DEMO_H
const int num=10; // const可以在.h中实现
#endif
/* main.cpp */
#include "demo.h"
#inculude <iostream>
int main()
{
cout<<num<<endl;
return 0;
}
extern const – 多文件
/* == demo.h == */
#ifndef _DEMO_H
#define _DEMO_H
// 相当于 extern 让const常量的可见范围恢复至整个项目
extern const int num=10; // const可以在.h中实现
#endif
/* == demo.cpp == */
#include "demo.h"
const int num=10;
/* == main.cpp == */
#include "demo.h"
#inculude <iostream>
int main()
{
cout<<num<<endl;
return 0;
}
new 二维数组 – 还没看
异常处理
try - catch
try有异常执行catch;没有异常就不执行catchq
异常可以发生在当前的try语块中,也可以发生在try语块所调用的某个函数中,或者更深的嵌套中
try
{
// 可能抛出异常的语句
}
catch(exceptionType variable)
{
// 处理异常的语句
}
#include <iostream>
#include <string>
#include <exception>
using namespace std;
void func()
{
throw "func unknown exception"; // throw关键字抛出异常
cout<<"func exception"<<endl; // 不会被执行
}
int main()
{
try
{
// func();
throw "main unknown exception";
cout<<"main exception"<<endl; // 不会被执行
}
catch(const char *&e) // 字符串引用
{
cout<<e<<endl;
}
return 0;
}
异常类型
当程序抛出异常时会创建一份异常数据,传递给与exceptionType类型匹配的 variable
catch(exceptionType variable)
表示catch可以处理什么类型的异常 variable是一个变量,用来接收异常消息
异常类型可以是任何基本类型或聚合类型
对于标准库中的函数抛出的异常,都是exception类或其他子类的异常,即抛出异常时会创建一个exception类或其子类的对象
catch(exceptionType variable)
可以看做是一个函数 接收异常数据
catch(exceptionType)
可以不处理异常数据
多级catch
try
{
/* code */
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
throw
catch(exceptionType variable)
表示catch可以处理什么类型的异常 variable是一个变量,用来接收异常消息
异常类型可以是任何基本类型或聚合类型
throw 可以是任意类型的数据(包括类的实例对象)
#include <iostream>
#include <string>
#include <exception>
using namespace std;
// 自定义异常
class OutOfRange
{
private:
int flag;
int len;
int index;
public:
OutOfRange();
OutOfRange(int len, int index);
void what() const; // 获取具体的错误信息
};
OutOfRange::OutOfRange()
{
this->flag = 1;
}
OutOfRange::OutOfRange(int len, int index)
{
this->len = len;
this->index = index;
this->flag = 2;
}
void OutOfRange::what() const
{
if (this->flag == 1)
{
cout << "ERROR: empty array" << endl;
}
else if (this->flag == 2)
{
cout << "ERROR: out of range" << endl;
}
else
{
cout << "ERROR: unknown exception" << endl;
}
}
class Array
{
private:
int len; // 数组长度
int capacity; // 当前的内存能容纳多少个元素
int *p; // 内存指针
/*
静态成员变量,必须在类外赋值,不赋值时默认为0
静态成员变量的访问收public、private、protected权限的限制
*/
static const int stepSize; // 每次扩容的步长
public:
Array();
~Array();
int operator[](int idx) const; // 运算符重载
int push(int item);
int pop();
int length() const;
};
const int Array::stepSize = 50;
Array::Array()
{
this->p = (int *)malloc(sizeof(int) * this->stepSize);
this->capacity = this->stepSize;
this->len = 0;
}
int Array::operator[](int index) const
{
if (index < 0 || index > this->len)
{
throw OutOfRange(this->len, index); // 抛出一个异常对象,匿名对象
}
return *(this->p + index);
}
int Array::push(int item)
{
if (this->len >= this->capacity)
{
this->capacity += this->stepSize;
this->p = (int *)realloc(this->p, sizeof(int) * this->capacity);
}
*(this->p + this->len) = item;
this->len++;
return this->len - 1;
}
int Array::pop()
{
if (this->len==0)
{
throw OutOfRange(); // 创建一个匿名对象
}
this->len--;
return *(this->p+this->len);
}
int Array::length() const
{
return this->len;
}
void printArray(const Array &arr)
{
int len=arr.length();
if (len==0)
{
cout<<"empty"<<endl;
return ;
}
for (int i=0;i<len;i++)
{
if (i==len-1)
{
cout<<arr[i]<<endl;
}
else
{
cout<<arr[i]<<", ";
}
}
}
Array::~Array()
{
free(this->p);
}
int main()
{
Array arr;
for (int i=0;i<10;i++)
{
arr.push(i);
}
printArray(arr);
// 制造out of range
try
{
cout<<arr[20]<<endl;
}
catch(OutOfRange & e)
{
e.what();
}
// 制造empty
try
{
for (int i=0;i<20;i++)
{
arr.pop();
}
}
catch(OutOfRange &e)
{
e.what();
}
printArray(arr);
return 0;
}
多文件编译
多文件编写
.h
文件:存放常量、函数的声明、类的声明
.cpp
文件:存放变量、函数的定义、类的实现
对于类的定义,成员函数实现可以写在类定义的内部,然后放在头文件中
对于类的定义,成员函数的定义不再类定义内部实现的情况下,不能早头文件中定义
/* student.h */
class Student
{
private:
/* data */
public:
Student();
~Student();
const char *name;
int age;
float socre;
void print();
};
/* student.cpp */
#include <iostream>
#include "student.h"
using namespace std;
Student::Student()
: name(NULL)
{
this->age=0;
this->socre=0.0;
}
void Student::print()
{
cout<<this->name<<" "<<this->age<<" "<<this->socre<<endl;
}
Student::~Student()
{
}
/* main.cpp */
#include "student.h"
int main()
{
Student *pstu=new Student;
pstu->name="zhangsan";
pstu->age=10;
pstu->socre=20.2;
pstu->print();
delete pstu;
return 0;
}
防止头文件包含
常用的系统库文件,都利用上面的方法实现了避免重复包含
使用宏定义
ifndef/define/endif
编译效率低,可移植性高
/* student.h */
#ifndef _STUDENT_H
#define _STUDENT_H
class Student
{
};
#endif
/* school.h */
#include "student.h"
class School
{
};
/* main.cpp */
#include "student.h"
#include "school.h"
int main()
{
return 0;
}
pragma once
#pragma once
添加到指定文件的最开头位置,则该文件就值会被#include一次
缺点: 只能作用于整个文件,不能像宏定义一样作用于指定的一段代码
编译效率高,可移植性差
/* student.h */
#pragma once
class Student
{
};
联合使用
```cpp
/* student.h */
#pragma once
#ifndef _STUDENT_H
#define _STUDENT_H
class Student
{
};
#endif