C++ part2
为什么析构函数必须是虚函数?为什么C++默认的析构函数不是虚函数?
将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。
C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。
函数指针
1、定义:
函数指针是一个指向具体的函数的指针变量。
C++在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。
2、用途:
调用函数和做函数的参数,比如回调函数。
void sort(int a[], bool (p)(int ,int)){}
void (fp)(int ,int) = cmp;
fork函数
references:
简述C语言fork()函数用法:
nowcoder
Ps.因为Windows是单用户系统(DOS),所以只能去UNIX/LInux系统上才能使用这个函数。
C++的fork函数用来创建主进程的子进程。子进程从父进程的fork()开始往下运行。在子进程中,成功的fork( )调用会返回0。在父进程中fork( )返回子进程的pid。如果出现错误,fork( )返回一个负值。
fork()
fork()
fork()
cout << 1 <<endl;
以上返回8个1。
用法:
1、一个父进程希望复制自己,使父子进程同时执行不同的代码段。
比如在网络服务程序中,父进程等待客户端的服务请求。当请求到达时,父进程调用fork()使子进程处理此请求;而父进程继续等待下一个请求。
2.、一个进程要执行一个不同的程序。
这个在shell下比较常见,这种情况下,fork()之后一般立即接exec函数。
析构函数
不能带任何参数,也没有返回值。
如果一个类中有指针,且在使用的过程中动态的申请了内存,那么最好显示构造析构函数在销毁类之前,释放掉申请的内存空间,避免内存泄漏。
类析构顺序:1)派生类本身的析构函数;2)对象成员析构函数;3)基类析构函数。
静态函数和虚函数的区别
静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定。
虚函数因为用了虚函数表机制,调用的时候会增加一次内存开销。
重载,重写,隐藏
references:
C++中重载、重写(覆盖)和隐藏的区别
重载:两个函数名相同,但是参数列表不同(个数,类型),返回值类型没有要求。
重写:派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。基类中被重写的函数必须是虚函数。
隐藏:派生类的函数屏蔽了与其同名的基类函数,注意只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。
如果派生类一定要使用基类的被隐藏函数,可以void f() { Base::f(); }调用。
strcpy和strlen
strcpy是字符串拷贝函数,原型:
char *strcpy(char* dest, const char *src);
从src逐字节拷贝到dest,直到遇到'\0'结束,因为没有指定长度,可能会导致拷贝越界,造成缓冲区溢出漏洞,安全版本是strncpy函数。
strlen函数是计算字符串长度的函数,返回从开始到'\0'之间的字符个数。
虚函数和多态
多态的实现主要分为静态多态和动态多态,静态多态主要是重载,在编译的时候就已经确定;动态多态是用虚函数机制实现的,在运行期间动态绑定。
虚函数的实现:在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(.text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。
++i和i++的实现
写一个函数在main函数执行前先运行
全局变量可以在main函数之前调用析构函数。
static int _ = []{
cin.sync_with_stdio(false);
return 0;
}();
__attribute((constructor))是gcc扩展,标记这个函数应当在main函数之前执行。同样有一个__attribute((destructor)),标记函数应当在程序结束之前(main结束之后,或者调用了exit后)执行;
__attribute((constructor))void before()
{
printf("before main\n");
}
const char * arr = "123"; char * brr = "123"; const char crr[] = "123"; char drr[] = "123";的区别
常量字符串、常量字符数组。
C++里是怎么定义常量的?常量存放在内存的哪个位置?
常量在C++里的定义就是一个top-level const加上对象类型,常量定义必须初始化。
对于局部对象,常量存放在栈区,对于全局对象,常量存放在全局/静态存储区。对于字面值常量,常量存放在常量存储区。
const修饰成员函数的目的
const修饰的成员函数表明函数调用不会对对象做出任何更改。
shared_ptr的实现
#include<iostream>
using namespace std;
template<class T>
class Shared_ptr{
public:
Shared_ptr() : ptr(nullptr), u_count(0){}
Shared_ptr(T* obj) : ptr(obj), u_count(new int(1)){}
Shared_ptr(const Shared_ptr &obj) : ptr(obj.ptr), u_count(&(++(*obj.u_count))){}
~Shared_ptr(){
--(*u_count);
if(*u_count == 0 && ptr){
delete u_count;
delete ptr;
}
}
Shared_ptr& operator = (Shared_ptr &o);
T& operator * ();//解引用
int use_count();
private:
T* ptr;
int* u_count;
};
template<class T>
Shared_ptr<T>& Shared_ptr<T>::operator = (Shared_ptr &o){
if(this == &o){ //特别注意
return *this;
}
++*o.u_count;
--this->u_count;
if(this->u_count == 0){
delete this->ptr;
delete this->u_count;
}
this->ptr = o.ptr;
this->u_count = o.u_count;
return *this;
}
template<class T>
T& Shared_ptr<T>::operator * (){
if(u_count == 0) return nullptr;
return *ptr;
}
template<class T>
int Shared_ptr<T>::use_count(){
return *u_count;
}
class A{
public:
A(){}
~A(){}
};
int main(){
Shared_ptr<A> a(new A());
cout << a.use_count() << endl;
Shared_ptr<A> b = a;
cout << b.use_count() << endl;
Shared_ptr<A> c(b);
cout << c.use_count() << endl;
return 0;
}
class与typename的区别
references:
c++中模板使用时候typename和class的区别
template<class T>
void MyMethod( T myarr )
{
typedef typename T::LengthType LengthType;
LengthType length = myarr.GetLength;
}
此时,如果没有typename,那么编译器会认为T::LengthType是T类的一个静态变量,加了之后他会认为T::LengthType是一个类型名称。