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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值