前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++基本语法

C++基本语法

作者头像
歪歪梯
发布2020-08-17 17:35:48
9910
发布2020-08-17 17:35:48
举报
文章被收录于专栏:歪歪梯Club歪歪梯Club

引言

C++ primer plus的知识笔记,以下内容基于c++11标准

指针与&、*

&在c++中的一种作用为取出当前变量在内存的逻辑地址 *在c++中的一种作用为取出当前逻辑地址对应的内存空间的值 c++中提供指针变量存储对象的地址,指针的运算会被编译器优化为地址的运算,比如一个int类型的指针 p+1的值实际是p指向的地址+1个int所占空间大小后的逻辑地址 指针语法为 typename * variable,比如

代码语言:javascript
复制
int a = 5;
int * p = &a;
*p == 5;//true

而对于结构体指针,可以使用->来操作成员,如

代码语言:javascript
复制
struct data{
    int a = 1;
}
data d1 = {
    a:2
};
data * dp1 = &d1;
d1.a = 3;
dp1->a = 5;
(*dp1).a = 6;

宏定义与内联函数

c++中可以在文件头使用#define进行宏定义,编译器在编译时会将代码里特定字符串替换为宏定义以后的结果,这也是内联函数的实现原理(第6版书中255页,8.1小节),所以内联函数会比较占据内存(多个代码副本),也就是调用内联函数的地方,会被编译器替换为内联函数的执行代码,与宏定义的函数区别是,内联函数更加的严谨,其限定了参数类型及返回值类型 比如

代码语言:javascript
复制
#define eetal 1
int a = eetal;//compile as int a = 1;
#define sum(x,y) (x+y)
int b = sum(1,2);//compile as int b = (1+2)
inline int sum(int a,int b){
           return a+b;
}

因为宏定义会对文本替换,一般用于定义常量等,为了避免重复宏定义,c++提供了#ifndef(if not define缩写)命令来判断当前是否进行了某个名称的宏定义,可以根据结果进行处理 如:

代码语言:javascript
复制
 #ifndef eetal
     //code
 #endif

同时还有用于判断宏定义的#if

代码语言:javascript
复制
#define a 5
#if a>5
    ...
#endif

以上代码代表如果没有定义过eetal这个宏变量,则会执行#ifndef和最近的endif之间的代码

类型别名

与宏定义类似的,c++还提供了typedef可以对类型取别名和定义一些函数指针的别名 比如

代码语言:javascript
复制
typedef int iint;
typedef void (*functionPointer)(int a);//接下来代码中

在上述代码之后的代码中使用iint定义变量等价于使用int 使用functionPointer作为类型可以定义指向 返回值为void,只有一个int形参的函数指针 c++11标准给using指令也加入了取别名的用法(真希望可以废弃一些旧的用法,太多重复的东西)。上述代码等价于

代码语言:javascript
复制
using iint = int;
using functionPointer = void (*)(int a);

泛型

c++的泛型通过template来设定,泛型方法代表该方法尚未注册实际代码,只有在代码里调用了该方法时,会通过隐式触发或者显示定义或者主动触发来创建对应的方法实例 方法的匹配规则为 代码中指定泛型方法>普通方法>显示声明>泛型方法 如

代码语言:javascript
复制
//在一些标准好像typename也会写为class,如下的两种写法swap01和swap02一样
template<class T> void swap02(T &a,T &b){
    T temp = b;
    b = a;
    a = temp;
}

template<typename T> void swap01(T &a,T &b){
    T temp = b;
    b = a;
    a = temp;
}

//泛型的显示声明,匹配规则(代码中指定泛型方法>普通方法>显示声明>泛型方法)
template<> void swap01<int>(int &a,int &b);

此时调用如

代码语言:javascript
复制
int a,b;
swap01(a,b);
//普通调用,先匹配显示声明

double c,d;
swap01(c,d);
//显示声明不匹配类型,触发隐式生成double泛型的方法实例匹配

char e,f;
swap01<char>(e,f);
//主动指定方法泛型为char

decltype

因为泛型会导致方法的不安全,比如

代码语言:javascript
复制
template<typename T> void sum(T &a,T &b){
    ? c = a+b;
    //do something
}

因为c++重载了运算符,string类型也可以使用+拼接,那如何确定泛型变量运算返回值的类型呢?于是有了decltype

代码语言:javascript
复制
decltype(a+b) c = a+b;

上述代码代表,如果a+b的表达式合法,c的类型即为他们运算后值的类型,deltype还有一个特殊用法来创建引用

代码语言:javascript
复制
int a;
decltype((a)) sa = a;//equals to  int & sa = a;

上述代码代表sa的类型为 a的类型 的引用类型,即变量sa此时是a的一个引用

const与mutable

const

const代表常量定义(java里的final),不可修改。因为cpp里面的指针类型定义比较特殊,所以const的位置代表不同含义,如

代码语言:javascript
复制
const int v = 0;//v cantnot be change anymore

struct data{
    int a;
};

....

data t1 = {
    a = 0;
},t2 = {
    a = 3;
};

const data * p1 = &t1;
//p1->a = 4     cannot work, error
//p1 = p2      it work

data * const p2 = &t2;
//p2->a = 4     it work
//p2 = p1      cannot work, error

如上述代码,p1可以修改指向的地址,但是无法通过p1修改单元内容 p2不可修改指向的地址,但是可以通过p2修改单元内容

mutable

mutable用于标记结构体中不想受结构体的const限制的成员,比如

代码语言:javascript
复制
const struct data{
    int a = 1;
    mutable int b = 0;
}
data * d = new data();
d->b = 3;//ok
//d->a = 5;error

上述代码创建了一个结构体data,因为其以const修饰,成员a不能被修改,而因为成员b以mutable修饰,不受限制可以修改

volatile

与java一样消除内存屏障用的,标记告诉编译器不要缓存该变量到寄存器或者线程缓存,每次从内存读取

代码语言:javascript
复制
volatile int a;

thread_local

用于创建存放在当前线程作用域的对象,该对象存放在当前线程内存,在当前线程存活时间里保持存活

代码语言:javascript
复制
thread_local int a = 1;

register

在c++11标准里代表默认,类似java的default(在以前标准为通知编译器尽量存储到寄存器,不过c++11改了)

auto

在c++11标准代表类型推断,必须在定义时为变量完成初始化,否则无法推断(在旧版本标准为默认,即当前的register作用)

代码语言:javascript
复制
auto a = 1;
//auto b; error

static与extern

在c++中,在文件的函数外部,及全局便写的变量为全局变量,其他连接的工程文件在代码里可以通过extern关键字引入其他文件的全局变量。 而static代表标记变量为静态的全局变量,不能被extern发现 全局变量的生命周期时整个程序运行期间

代码语言:javascript
复制
//1.cpp
int A = 100;
static int B = 5;

代码语言:javascript
复制
//2.cpp
extern int A;//100
//extern int B; error

枚举

c++的枚举存储整数,不指定默认第一个为0后续每个依次递增,整数不能直接赋值给枚举变量,需要通过构造,而枚举变量可以直接赋值给整数

代码语言:javascript
复制
enum langs{java, cpp};// 0 and 1
//enum langs{java=3, cpp=100};// 3 and 100
langs newLang = langs(1);//cpp
langs anotherLang = java;
int howMuch = anotherLang;//0

new与delete

cpp中也是使用new来创建对象(在cpp中比较习惯称为分配内存,c里还有malloc,calloc,realloc),同时提供释放内存的指令,这会造成很多危险,但是也带来性能的提升

使用new申请内存

代码语言:javascript
复制
#include<new>
int * p = new int();//只申请一个int的空间,返回地址
int * ap = new int[5];//申请一个5个int元素的数值空间,返回第一个元素地址
int * p2 = new(p) int();//指定要分配空间的首地址,此种方式要引入new头文件

如上代码,其中ap为数组头,而p2指定了首地址为p1,这样代表其分配的空间覆盖了p1的空间,当尝试申请的内存不够时,在c++11中会抛出异常(以前是返回空地址0)

使用delete释放内存

对于普通指针,使用delete 指针删除,而对于数组类型的指针,应当使用delete[]来调用数组各个元素的析构函数(基本类型只是释放),因为p1已经被p2覆盖,所以释放了p2就不能再去释放p1,因为内存已经被回收了

代码语言:javascript
复制
delete p2;
delete[] ap;

命名空间

c++因为大部分代码还是存在面向过程,放在全局的变量的做法,为了方便管理避免重名,引入命名空间,通过命名空间归类方法和变量,比如

代码语言:javascript
复制
namespace std{
istream cin;
ostream cout;
}

上述代码创建了一个std的命名空间,里面的对象通过 命名空间::成员名称 使用,比如

代码语言:javascript
复制
std::cin;
std::cout;

通过使用using命令,指定将命名空间内容加入当前代码块,则可以省略命名空间,如

代码语言:javascript
复制
using namespace std;
cin;
cout;

命名空间还可以嵌套

代码语言:javascript
复制
namespace yyt{
    namespace std{
        istream cin;
        ostream cout;
    }
}
....
yyt::std::cin;

对于本文件的全局变量,可以使用匿名命名空间访问成员,如

代码语言:javascript
复制
#include<iostream>
namespace eetal{
    int a = 10;
}
int a = 5;
int main(){
    int a = 3;
    std::cout<<a<<endl;//3
    std::cout<<::a<<endl;//5
    std::cout<<eetal::a<<endl;//10
    return 0;
}

文件IO

文件IO需要通过引入头文件fstream

代码语言:javascript
复制
#inlcude<fstream>
//输出流ofstream
ofstream outPutStream;
outPutStream.open("test.txt");//will clear old content
outPutStream.write("aaas",4);//writer max 4 characters
outPutStream.flush();
outPutStream.close();

代码语言:javascript
复制
//输入流ifstream,
 ifstream inputStream;
 inputStream.open("test.txt");
 char str[5];
 inputStream.read(str,4);//read max 4 characters
 inputStream.close();

array和vector初探

array是有界的,vactor无界(自动增长),并且可以直接像数组一样使用

代码语言:javascript
复制
#include<array>
#include<vector>

int size = 5;
vector<double> doublevWithSize(size);//size is a variable
array<int,5> array1;//cannot use variable the size must is const
doublevWithSize[0] = array1[0];
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 歪歪梯Club 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 指针与&、*
  • 宏定义与内联函数
  • 类型别名
  • 泛型
  • decltype
  • const与mutable
    • const
      • mutable
      • volatile
      • thread_local
      • register
      • auto
      • static与extern
      • 枚举
      • new与delete
        • 使用new申请内存
          • 使用delete释放内存
          • 命名空间
          • 文件IO
          • array和vector初探
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档