(1)名称空间
定义自己的名称空间函数:
namespace mycode{
void foo()
{
cout<<1<<endl;
}
}
int main()
{
mycode::foo();
return 0;
}
还可以直接using mycode::foo;进行声明之后直接使用。
(2)std::array是c++的大小固定的特殊容器,知道自己的大小,不会自动转换为指针,具有迭代器;
数组大小是固定的,如果要使用动态大小,就要使用vector
(3)基于区间的for循环
方便的迭代容器中的元素,多用于STL中的容器,如:
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
for(int i : v)
{
cout<<i<<endl;
}
(4)const用法:
定义常量;保护参数;const引用参数(引用可改,const保证不改,这样做是为了保证效率,没有副本)
const和#define相比的优点
const有类型,可以执行类型检测,更安全;
#define会有多次拷贝复制,消耗内存;
const实现方式:在编译期间实现替代,类似于#define;
(5)不能引用常量,除非const引用;
(6)对引用取地址的值和对变量取地址的值相同;
(7)无法声明引用的引用以及指向引用的指针,因为引用本身不是一个实体;
(8)如果一个类里面有引用的数据成员,那么必须在构造函数初始化器(构造函数初始化列表)中初始化引用数据成员:
class Myclass
{
public:
Myclass(int& ref):mRef(ref){}
private:
int& mRef;
};
(9)返回变量的引用的时候,必须保证变量作用域不限于本函数;
(10)&&用以定义右值引用,可以用来解析临时变量,什么是右值?(非左值的,运算符右边的那个,表达式,常量等等...)
(11)constexpr关键字用于定义常量表达式。
(12)extern 和 static
extern将后面跟的名称指定为外部链接;
static :①全局性②初始化一次③记忆性④作用域限定
作用:1.用于静态变量或函数的声明,变量和方法属于类,不属于任何一个对象;
2.用于函数中的静态变量,函数内部的全局访问变量,每次调用离开函数都保存值,可以有标记作用;
(普通变量在超出作用域的时候,会进行释放;而静态变量超出作用域只是不能访问,当不会释放,下次再次进入到作用域当中的时候,会访问到之前的值。)
3.静态链接:外部链接意味名称在其他源文件中有效;内部链接(静态链接)表示在其他文件中无效;
默认情况,函数和全局变量都有外部链接,可以在声明前面加static指定内部链接;
static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
static和const变量的初始化问题:
static变量在类内部声明,必须在类外部定义和初始化;const常量在类内部声明,必须在类的构造函数初始化列表中进行初始化(为什么const不在类里面进行初始化?因为类的const变量只有在某个对象的生存区间里面是不变的,但是类的不同对象的const值是可以不同的,所以最好是构建对象的时候进行初始化)。
初始化的时候不带static,但是要带const:
class A{
public:
A(num):a(num){...}
private:
const int a;
static int b;
const static int c;
}
int A::b = 0;//由于此处是定义+初始化,所以要加上类型描述符(不用加static)
const int A::c = 0;
(13)typedef关键字,为已有的类型声明提供新名称(模板中常用):
typedef int* IntPtr;给int型指针定义一个别名,定义之后,int* p1和IntPtr p2 的声明是等价的;
函数指针:typedef int (*FuncType)(char ,double);typedef定义一个函数指针(指向一个函数)类型FuncType
这个函数的原型是:int func(char s, double b);//函数指针可以自动解除指针引用
等价于使用类型别名:using FuncType= int (*)(char,double);
(14)size_t类型保证能容纳实现所建立的最大对象的字节大小,一般为无符号整型
(15)\n和endl的区别:\n仅仅开始新行,endl会刷新缓存区;
(16)auto关键字:可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型(必须初始化);
(17)名称解析的工作方式:首先在局部作用域中解析,然后在类的作用域中解析,最后在全局作用域中解析。
(18)C++中的类型转换(C中()转换方式仍然可以用,但是不安全)
const_cast:删除一个const常量的const特性(当需要将一个const常量给一个非const作参数时,可以使用)
使用方式:假设A是一个类,a是A的一个const对象const_cast<A&>a可以消除其const特性,注意只能用A&或者A*的形式,不能直接用A。
static_cast:
int i; double j=static_cast<double>(i);//类型转换
还可以用于在继承层次结构中执行向下转换
reinterpret_cast:不安全的转换;没有类型检测;
dynamic_cast:可以用来执行继承层次结构的类型转换(指针或者引用),执行时进行了类型检测,较安全;
如果转换没有意义,返回空指针(指针转换),异常(引用转换);
为了使用,类必须至少包含一个虚方法,否则会报错;因为类型信息存储在对象的虚表中;
(19)文件流(流:不仅包含数据,还有当前位置)
<fstream>中的ofstream和ifstream类提供对文件的输出和输入功能;
(20)sizeof只算堆和栈分配的大小,故而如果是static类型,则不算;
(21)指针和引用
指针是实体,存放地址;引用不是一个实体,其实只是一个别名;
指针可以为空,引用不能为空,这一点决定引用代码效率更高;
指针可以重新赋值,引用不能重新赋值;引用必须在定义的时候进行初始化;
有多重指针;无多重引用;
传参的时候;指针传参的时候,也会有指针的拷贝,生成一个副本;
指向内容不变的时候,应该用引用;指向可变或者要指向空的时候用指针(有const指针但是没有const引用,因为引用本身不可变);
不能返回局部变量的引用,不能返回函数内部new分配内存的引用(没办法回收,内存泄漏)
(22)inline函数
对于那些频繁被调用,函数步骤比较简单的函数,可以用inline关键字声明成内联函数。
内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。这样可以节省函数调用时的调用开销。并且编译器还会对其执行优化,这些优化对于outline不会发生。
缺点:增加目标码大小,故不能乱用
成员函数和inline函数:
①如果在类体中定义的成员函数中不包括循环等控制结构,C++系统会自动将它们作为内置(inline)函数来处理。
②如果成员函数不在类体内定义,而在类体外定义,系统并不把它默认为内置(inline )函数,调用这些成员函数的过程和调用一般函数的过程是相同的。如果想将这些成员函数指定为内置函数,应当用inline作显式声明。
③内联与否,只是你的建议,是否真正内联,得看编译器的意思,写了inline,或者你把类方法实现写在头文件,意味着你希望编译器帮你内联,但是是否真正内联,要看编译器,如果你的内联函数写得很复杂,编译器不一定内联;
内联函数和宏定义:
①宏定义只是展开,内联函数会有参数类型检查,相对较为安全
②宏是预处理器处理,内联函数是编译器控制
(23)new创建数组的时候,会多分配4个字节的内存用来存放数组的大小,以便于能够正确地delete。
(24)C语言中的FILE*结构在c++中不能直接使用
可以通过:int fd = fp->fd;
FCB *fcb;
string fileName = fcb[fd].filename;
获取到一个FILE*结构的文件名,然后按照FILE*中的状态调用iostream的open函数将其转换成C++中的流。
(25)并不是所有的指针大小都是4个字节。指向成员函数的指针就是一个例外。
(26)用const实现函数重载
class TextBlock{
public:
TextBlock(string str){
this->str = str;
}
const char& getchar(int position)const{
cout << "const getchar"<<endl;
return str.at(position);
}
const char& getchar(int position){
cout << "getchar"<<endl;
return str.at(position);
}
private:
string str;
};
int main()
{
TextBlock* t1 = new TextBlock("abc");
cout << t1->getchar(1) << endl; //调用getchar版本
const TextBlock* t2 = new TextBlock("abc");
cout << t2->getchar(1) << endl; //调用getchar的const版本
return 0;
}
(27)除了内置类型和STL迭代器之外,其他的类型应当尽量使用引用传参。
为什么内置类型采用值传递?因为引用传参的底层是通过指针实现的。
(28)当函数需要返回一个对象的时候,会有两种选择:
①直接by-value返回一个对象
A func()
{
A a(2);
return a;
}
这样不会出错,尽管a在函数内部创建,但是会有一个拷贝构造的过程。
但是,这样的效率比较低,因为return 的时候返回的是a的副本(拷贝构造函数返回的)。
上面仅仅是理论分析,真的是这样吗?????
代码测试:
#include <iostream>
using namespace std;
class A{
public:
A(){}
A(int v):val(v){}
A(const A& a){
cout<< "copy construct!"<<endl;
this->val = a.val;
}
int getVal()
{
return this->val;
}
private:
int val;
};
A func()
{
A a(2);
return a;
}
int main()
{
cout << func().getVal() << endl;
return 0;
}
在codeblocks里面和直接g++进行编译,结果是都没有调用拷贝构造函数,也就是并没有输出"copy construct!"
网上查找原因是:编译器优化掉了多余的拷贝构造,直接进行了值的拷贝。
②by-reference返回一个对象
采用这种方式可能会出现错误,(1)reference指向栈中对象----离开作用域对象会被释放(2)reference指向堆中的对象-------谁来回收?容易内存泄漏(3)reference指向static对象-------非线程安全
好的做法是,直接采用①中的方法,让编译器进行优化
关于IO流
三类头文件以及对应的流类型:
<iostream>-标准输入输出流
几种类:istream:从流读数据(cin是一个istream类的对象,从标准输入读)
特别的,istream的对象有>>运算符
ostream:向流写数据(cout和cerr都是ostream类的对象,向标准输出写)
特别的,ostream的对象有<<运算符
iostream:既可读又可写的流
<fstream>-文件流
几种类:ifstream(继承自istream):从文件读数据
ofstream(继承自ostream):向文件写数据
fstream(继承自iostream):读写文件
两种打开文件的方式:
①ofstream outFile; //先定义流
outFile.open("fileName" , mode);//然后调用对象的open函数打开文件
②ofstream outFile("fileName",mode);//直接在定义的时候指定关联的文件以及打开的模式
mode 有:
ios::in 为输入(读)而打开文件
ios::out 为输出(写)而打开文件
ios::ate 初始位置:文件尾
ios::app 所有输出附加在文件末尾
ios::trunc 如果文件已存在则先删除该文件
ios::binary 二进制方式
可以调用流的is_open()方法判断是否成功打开一个文件
打开之后,可以用<<和>>分别写读一个文件,也可以getline()等方法。
使用完毕之后调用流的close方法关闭文件
<sstream>-string流
几种类:istringstream(继承自istream):从string读数据
ostringstream(继承自ostream):向string写数据
stringstream(继承自iostream):读写string
使用string流的时候也是有两种方法将一个string拷贝给一个流:
sstream strm(string str);
istringstream使用场景:首先进行单行处理,然后单行里面需要对单个单词进行处理的情况,就可以将一行内容转换成istringstream。
例如:
string line;
while(getline(cin,line)){
istringstream record(line);//将读入的一行内容转换成字符串流
while(line >> word){
/*对单行中的每个单词进行处理*/
}
}
ostringstream使用场景:和istringstream类似,可以先对每个string处理后写入一个ostringstream流中,最后一起输出
例如:
ostringstream result;
while(cin>>word){
result << " " << word << " ";
}
cout << result << endl;
面试题:C++和java的区别?
①编译方面。
C++是编译型语言,编译器直接将其编译链接成操作系统可以执行的机器码(所以速度更快)
java是 编译+解释 型语言,编译成字节码(.class文件),然后由jvm虚拟机解释成机器码
所以在跨平台的实现上,C++是通过不同平台的不同编译器实现(重新进行编译),而java是通过不同的jvm解释实现,编译过程是相同的(甚至直接可以跨平台使用class文件)。
②内存方面
C++在内存上使用了指针,用户可以自己灵活控制内存的使用,也增加了内存泄漏的可能。
java是jvm底层控制内存,用户只能使用引用,还有自动的内存回收机制。
③继承
c++有多重继承,java没有。
接口的方式不一样,java有接口抽象类,c++只有抽象基类。
java完全面向对象,数据必须在类中,但是C++可以定义全局变量
④c++可以操作符重载,java不支持
⑤预处理功能
Java不支持预处理功能。c/c++在编译过程中都有一个预编泽阶段,即众所周知的预处理器。预处理器为开发人员提供了方便,但增加了编译的复杂性。JAVA虚拟机没有预处理器,但它提供的引入语句(import)与c++预处理器的功能类似。
⑥C++模板编程,java是泛型