如果同时定义了两个函数,一个带const,一个不带,会有问题吗?
- 不会,这相当于函数的重载
#include<iostream>
class A{
public:void print()const{std::cout << "Hello" << std::endl;}void print(){std::cout << "World" << std::endl;}
};
int main(){A a{};const A const_a;a.print();const_a.print();return 0;
}
请你来说一说隐式类型转换
- 首先,对于内置类型,低精度的变量给高精度变量赋值会发生隐式类型转换, int -> double
- 其次,对于只存在单个参数的构造函数的对象构造来说,函数调用可以直接使用该参数传入,编译器会自动调用其构造函数生成临时对象
注意事项
- “可以用单个形参进行调用” 并不是指构造函数只能有一个形参,而是它可以有多个形参,但那些形参都是有默认实参的。 那么,什么是“隐式转换”呢? 上面这句话也说了,是从 构造函数形参类型 到 该类类型 的一个编译器的自动转换。
- 代码中可以看到,isSameISBN函数是期待一个BOOK类类型形参的,但我们却传递了一个string类型的给它,这不是它想要的啊!还好,BOOK类中有个构造函数,它使用一个string类型实参进行调用,编译器调用了这个构造函数,隐式地将stirng类型转换为BOOK类型(构造了一个BOOK临时对象),再传递给isSameISBN函数。 隐式类类型转换还是会带来风险的,正如上面标记,隐式转换得到类的临时变量,完成操作后就消失了,我们构造了一个完成测试后被丢弃的对象。 我们可以通过explicit声明来抑制这种转换:
- explicit BOOK(string ISBN,float price=0.0f):_bookISBN(ISBN),_price(price){}
- explicit关键字只能用于类内部的构造函数声明上.这样一来,BOOK类构造函数就不能用于隐式地创造对象了
- 可以使用一个实参进行调用,不是指构造函数只能有一个形参。 隐式类类型转换容易引起错误,除非你有明确理由使用隐式类类型转换,否则,将可以用一个实参进行调用的构造函数都声明为explicit。
- explicit只能用于类内部构造函数的声明。它虽然能避免隐式类型转换带来的问题,但需要用户能够显式创建临时对象(对用户提出了要求)。
#include <string>
#include <iostream>
using namespace std ;
class BOOK //定义了一个书类
{
private:string _bookISBN ; //书的ISBN号float _price ; //书的价格public://定义了一个成员函数,这个函数即是那个“期待一个实参为类类型的函数”//这个函数用于比较两本书的ISBN号是否相同bool isSameISBN(const BOOK & other ){return other._bookISBN==_bookISBN;}//类的构造函数,即那个“能够用一个参数进行调用的构造函数”(虽然它有两个形参,但其中一个有默认实参,只用一个参数也能进行调用)BOOK(string ISBN,float price=0.0f):_bookISBN(ISBN),_price(price){}
};int main()
{BOOK A("A-A-A");BOOK B("B-B-B");cout<<A.isSameISBN(B)<<endl; //正经地进行比较,无需发生转换cout<<A.isSameISBN(string("A-A-A"))<<endl; //此处即发生一个隐式转换:string类型-->BOOK类型,借助BOOK的构造函数进行转换,以满足isSameISBN函数的参数期待。cout<<A.isSameISBN(BOOK("A-A-A"))<<endl; //显式创建临时对象,也即是编译器干的事情。}
说说你了解的类型转换
- reinterpret_cast:可以用于任意类型的指针之间的转换,对转换的结果不做任何保证
- dynamic_cast:这种其实也是不被推荐使用的,更多使用static_cast,dynamic本身只能用于存在虚函数的父子关系的强制类型转换,对于指针,转换失败则返回nullptr,对于引用,转换失败会抛出异常。(1)dynamic_cast是运行时处理的,运行时要进行类型检查,而其他三种都是编译时完成的;(2)不能用于内置基本数据类型间的强制转换;(3)使用dynamic_cast进行转换时,基类中一定要有虚函数,否则编译不通过;(4)dynamic_cast转换若成功,返回的是指向类的指针或引用;若失败则会返回NULL;(5)在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。向下转换的成败取决于将要转换的类型,即要强制转换的指针所指向的对象实际类型与将要转换后的类型一定要相同,否则转换失败。 下行转换,使用基类class 创建 其派生类对象的时候,新建的对象只可以使用 基类的相关public、protected内容 和 本派生类所创建的独有的方法
- 参考此例子
#include <iostream>class Shape{
public:Shape();virtual ~Shape();virtual double calcArea();protected:double i_mNumber;
};class Circle : public Shape{
public:Circle(double r);~Circle();
protected:double m_iR;
};
int main(){double x = 2.5;//static_castint y = (int)x;//C语言int z = static_cast<int>(x);//C++//dynamic_castShape *p = new Circle(2.0);Circle *q = dynamic_cast<Circle *>(p);//reinterpret_castint r = 1000;int *w = reinterpret_cast<int *>(r);//将数值转化为地址 很危险//const int c = 0;
// int *o = &x;//Errorint *g = const_cast<int *>(&c);
}
参考链接
- C++中四种cast(强制)转换
- const_cast:对于未定义const版本的成员函数,我们通常需要使用const_cast来去除const引用对象的const,完成函数调用。另外一种使用方式,结合static_cast,可以在非const版本的成员函数内添加const,调用完const版本的成员函数后,再使用const_cast去除const限定。
- static_cast:完成基础数据类型;同一个继承体系中类型的转换;任意类型与空指针类型void* 之间的转换。
参考链接
- 你必须知道的指针基础-8.栈空间与堆空间
- 栈空间和堆空间大小
请你来说一说C++函数栈空间的最大值
- 默认是1M,不过可以调整
- 补充:window和Linux不一样,linux如图stack size所示 为8kb,win为2kb;而堆本质是使用链表的形式申请内存空间,和操作系统的虚拟内存挂钩,一般大小为2G
请你来说一说extern“C”
- C++调用C函数需要extern C,因为C语言没有函数重载
参考链接
- extern “C”的作用详解
- extern "C":实现C++和C的混合编程
请你回答一下new/delete与malloc/free的区别是什么
- 首先,new/delete是C++的关键字,而malloc/free是C语言的库函数,后者使用必须指明申请内存空间的大小,对于类类型的对象,后者不会调用构造函数和析构函数
补充
- 1、new分配内存按照数据类型进行分配,malloc分配内存按照指定的大小分配;
- 2、new返回的是指定对象的指针,而malloc返回的是void*,因此malloc的返回值一般都需要进行类型转化。
- 3、new不仅分配一段内存,而且会调用构造函数,malloc不会。
- 4、new分配的内存要用delete销毁,malloc要用free来销毁;delete销毁的时候会调用对象的析构函数,而free则不会。
- 5、new是一个操作符可以重载,malloc是一个库函数。
- 6、malloc分配的内存不够的时候,可以用realloc扩容。new没用这样操作。
- 7、new如果分配失败了会抛出bad_malloc的异常,而malloc失败了会返回NULL。
- 8、申请数组时:new[]一次分配所有内存,多次调用构造函数,搭配使用delete[]。delete[]多次调用析构函数,销毁数组中的每个对象。而malloc则只能sizeof(int) * n。
参考链接
- 请你来回答一下new和malloc的区别
请你说说你了解的RTTI
- 运行时类型检查,在C++层面主要体现在dynamic_cast和typeid,VS中虚函数表的-1位置存放了指向type_info的指针。对于存在虚函数的类型,typeid和dynamic_cast都会去查询type_info
参考链接
- RTTI
- C++中的RTTI机制
- Chapter12:多态——从虚函数表到RTTI(二)
请你说说虚函数表具体是怎样实现运行时多态的?
- 子类若重写父类虚函数,虚函数表中,该函数的地址会被替换,对于存在虚函数的类的对象,在VS中,对象的对象模型的头部存放指向虚函数表的指针,通过该机制实现多态。
请你说说C语言是怎么进行函数调用的?
- 每一个函数调用都会分配函数栈,在栈内进行函数执行过程。调用前,先把返回地址压栈,然后把当前函数的esp指针压栈。
请你说说C语言参数压栈顺序?
- 从右到左
请你说说C++如何处理返回值?
生成一个临时变量,把它的引用作为函数参数传入函数内。
请你回答一下C++中拷贝赋值函数的形参能否进行值传递?
- 不能。如果是这种情况下,调用拷贝构造函数的时候,首先要将实参传递给形参,这个传递的时候又要调用拷贝构造函数。如此循环,无法完成拷贝,栈也会满。
请你回答一下malloc与new区别
- malloc需要给定申请内存的大小,返回的void* 指针需要强转
- new会调用构造函数,不用指定内存大小,返回的指针不用强转
请你说一说select
- select在使用前,先将需要监控的描述符对应的bit位置1,然后将其传给select,当有任何一个事件发生时,select将会返回所有的描述符,需要在应用程序自己遍历去检查哪个描述符上有事件发生,效率很低,并且其不断在内核态和用户态进行描述符的拷贝,开销很大
请你说说fork,wait,exec函数
- 父进程产生子进程使用fork拷贝出来一个父进程的副本,此时只拷贝了父进程的页表,两个进程都读同一块内存,当有进程写的时候使用写实拷贝机制分配内存
- exec函数可以加载一个elf文件去替换父进程,从此父进程和子进程就可以运行不同的程序了。fork从父进程返回子进程的pid,从子进程返回0.调用了wait的父进程将会发生阻塞,直到有子进程状态改变,执行成功返回0,错误返回-1。
- exec执行成功则子进程从新的程序开始运行,无返回值,执行失败返回-1
请你回答一下静态函数和虚函数的区别
- 静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定。虚函数因为用了虚函数表机制,调用的时候会增加一次内存开销,动态切换
请你说一说重载和覆盖
- 重载:两个函数名相同,但是参数列表不同(个数,类型),返回值类型没有要求,在同一作用域中
- 重写:子类继承了父类,父类中的函数是虚函数,在子类中重新定义了这个虚函数,这种情况是重写
请你说一说static关键字
- 1.加了static关键字的全局变量只能在本文件中使用。例如在a.c中定义了static int a=10;那么在b.c中用extern int a是拿不到a的值,a的作用域只在a.c中。
- 2.static定义的静态局部变量分配在数据段上,普通的局部变量分配在栈上,会因为函数栈帧的释放而被释放掉。
- 3. 对一个类中成员变量和成员函数来说,加了static关键字,则此变量/函数就没有了this指针了,必须通过类名才能访问
请你说一说strcpy和strlen
- strcpy是字符串拷贝函数,原型: char *strcpy(char* dest, const char *src);
- 从src逐字节拷贝到dest,直到遇到'\0'结束,因为没有指定长度,可能会导致拷贝越界,造成缓冲区溢出漏洞,安全版本是strncpy函数。
- strlen函数是计算字符串长度的函数,返回从开始到'\0'之间的字符个数。