【面试总结】小灰灰求职进行曲(二)C++语言方向
文章目录
- 1 C++ 三大特性、初始化顺序、构造和析构的顺序
- 2 重载(overload)、重写/覆盖(override)、final和override说明符
- 3 C++ void func() const(类成员函数,不允许修改类的数据成员)
- 4 C++ 模板 、全特化、偏特化
- 5 智能指针相关
- 6 new delete,malloc free 的区别
- 7 类的size,virtual 类的size
- 8 右值引用
- 9 c/c++中struct、static的区别
- 10 拷贝构造
- 11 禁止构造函数、禁止动态分配方式、显示构造、隐式构造
- 12 线程安全变量及关键字synchronized、volatile
- 13 C++空类编译器自动生成的6个成员函数
- 14 C++ 之xxxx_cast关键字的使用
- 15 虚继承如何解决了菱形继承的二义性问题
- 16
- 17
1 C++ 三大特性、初始化顺序、构造和析构的顺序
C++ 三大特性
封装,继承,多态
1 封装
隐藏类的属性和实现细节,仅仅对外提供接口。
优点
:隔离变化;便于使用;提高重用性;提高安全性;
缺点
:如果封装太多,影响效率;使用者不能知道代码具体实现。
封装性实际上是由编译器去识别关键字public、private和protected来实现的,体现在类的成员可以有公有成员(public),私有成员(private),保护成员(protected)。私有成员是在封装体内被隐藏的部分,只有类体内说明的函数(类的成员函数)才可以访问私有成员,而在类体外的函数时不能访问的,公有成员是封装体与外界的一个接口,类体外的函数可以访问公有成员,保护成员是只有该类的成员函数和该类的派生类才可以访问的。
2 继承
c++语言允许单继承和多继承
被继承的是父类(基类),继承出来的类是子类(派生类),子类拥有父类的所有的特性。
继承方式有公有继承、私有继承(默认),保护继承。
优点
:继承减少了重复的代码、继承是多态的前提、继承增加了类的耦合性;
缺点
:继承在编译时刻就定义了,无法在运行时刻改变父类继承的实现;父类通常至少定义了子类的部分行为,父类的改变都可能影响子类的行为;如果继承下来的子类不适合解决新问题,父类必须重写或替换,那么这种依赖关系就限制了灵活性,最终限制了复用性。
公有继承
中父类的公有和保护成员在子类中不变,私有的在子类中不可访问。
私有继承
中父类的公有和保护成员在子类中变为私有,但私有的在子类中不可访问。
保护继承
中父类的公有和保护成员在子类中变为保护,但私有的在子类中不可访问。
3 多态
多态性是指对不同类的对象发出相同的消息将会有不同的实现。
优点
:大大提高了代码的可复用性;提高了了代码的可维护性,可扩充性;
缺点
:易读性比较不好,调试比较困难; 模板只能定义在头文件中,当工程大了之后,编译时间十分的变态;
C++有两种多态,称为动多态(运行期多态)和静多态(编译器多态),静多态主要是通过模板来实现
,而动多态是通过虚函数来实现的
。即在基类中存在虚函数(一般为纯虚函数)子类通过重写这些接口,使用基类的指针或者引用指向子类的对象,就可以调用子类对应的函数,动多态的函数调用机制是执行器期才能确定的,所以他是动态的。
构造函数可以是虚函数吗?(不可以)
构造函数不能定义为虚函数,虚函数调用是在部分信息下完成工作的机制,允许我们只知道接口
而不知道对象的确切类型,要创建一个对象,需要知道想要创建的确切类型,虚函数的作用在于
通过父类的指针或引用来调用子类的那个成员函数,而构造函数是在创建对象时自己主动调用
的,不可能通过父类的指针或者引用去调用,因此,构造函数不应该被定义为虚函数。
析构函数可以是虚函数吗?(可以)为什么要将析构函数设置为虚函数
虚析构函数是为了解决父类指针指向子类对象时,释放子类对象的资源时,释放不完全,造成的内存泄漏
问题。如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放。假
设基类中采用的是非虚析构函数,当删除基类指针指向的派生类对象时就不会触发动态绑定,因
而只会调用基类的析构函数,而不会调用派生类的析构函数。那么在这种情况下,派生类中申请
的空间就得不到释放从而产生内存泄漏。所以,为了防止这种情况的发生,C++中基类的析构函
数应采用 virtual虚析构函数。
类中数据成员初始化顺序,构造和析构的顺序
基本原则
- 成员变量在使用初始化列表初始化时,与构造函数中初始化成员列表的顺序无关,只与定义成员变量的顺序有关。
- 如果不使用初始化列表初始化,在构造函数内初始化时,此时与成员变量在构造函数中的位置有关。
- 类中
const成员常量
必须在构造函数初始化列表中初始化。 - 类中
static成员变量
只能在类内外初始化(同一类的所有实例共享静态成员变量)
初始化顺序
- 基类的静态变量或全局变量
- 派生类的静态变量或全局变量
- 基类的成员变量
- 派生类的成员变量
构造和析构的顺序
父类构造函数–>成员类对象构造函数–>自身构造函数(从大的到小的,析构顺序相反)
2 重载(overload)、重写/覆盖(override)、final和override说明符
重载(overload)
即函数名相同,参数类型不同或参数个数不同,发生重载。
- 函数重载
- 构造函数重载
重写/覆盖(override)
**函数名相同,参数列表,返回值完全相同,**父类的虚函数或纯虚函数,子类重写该方法,运行时发生动态绑定。
- 重写(覆盖)父类方法
final和override说明符
- override (重写标识,可以检测是否错误)
- final(禁用继承类,禁止重写方法)
如果我们使用override标记了某个函数,优点(提示功能):
但该函数并没有覆盖已存在的虚函数,此时编译器将报错。
某个函数指定为 final ,意味着任何尝试覆盖该函数的操作都将引发错误。
3 C++ void func() const(类成员函数,不允许修改类的数据成员)
void func() const 的 const 表示该函数不能修改成员变量的值
class T_const{public:void addNum(){t_a = 33;cout << "addNum() -> " << t_a + t_b << endl;}void addNum_Const() const {// t_a = 33; // Error const 后置,表示该函数不可以修改成员变量cout << "addNum() -> " << t_a + t_b << endl;}void addOuter(int outer) const {outer = 11; // 可以修改形参cout << "addNum() -> " << t_a + t_b + outer << endl;}private:int t_a = 11;int t_b = 22;
};
4 C++ 模板 、全特化、偏特化
模板
泛型编程(不指定类型)
- 函数模板
- 类模板样例
函数模板是可以被重载的(类模板不能被重载),也就是说允许存在两个同名的函数模板。
//模板函数
template<typename T>
void add(T num1, T num2) {cout << num1 << " + " << num2 << " = "<< num1 + num2 << endl;
}//模板类
template<typename T>
class Test_Class {public:static void multi(T num1, T num2) {cout << num1 << " * " << num2 << " = "<< num1 * num2 << endl;}
};
模板为什么要特化,因为编译器认为,对于特定的类型,如果你能对某一功能更好的实现,那么就该听你的。
模板分为类模板与函数模板,特化分为全特化与偏特化。全特化就是限定死模板实现的具体类型,偏特化就是如果这个模板有多个类型,那么只限定其中的一部分。
全特化(类和函数,指定全部类型)
template<typename T1, typename T2>
class A{public:void function(T1 value1, T2 value2){cout<<"value1 = "<<value1<<endl;cout<<"value2 = "<<value2<<endl;}
};template<>
class A<int, double>{ // 类型明确化,为全特化类public:void function(int value1, double value2){cout<<"intValue = "<<value1<<endl;cout<<"doubleValue = "<<value2<<endl;}
};
偏特化(只能为类,指定部分类型)
template<typename T1, typename T2>
class A{public:void function(T1 value1, T2 value2){cout<<"value1 = "<<value1<<endl;cout<<"value2 = "<<value2<<endl;}
};template<typename T>
class A<T, double>{ // 部分类型明确化,为偏特化类public:void function(T value1, double value2){cout<<"Value = "<<value1<<endl;cout<<"doubleValue = "<<value2<<endl;}
};
重点总结
- 类模板能全特化、偏特化,不能被重载;
- 函数模板能全特化,不能被偏特化;
模板类调用优先级
全特化类 > 偏特化类 > 主版本模板类
5 智能指针相关
智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。
- auto_ptr
- shared_ptr
- unique_ptr
- weak_ptr
四种智能指针的原理及实现
#include <memory>
void t_ptr(){int a = 99;shared_ptr<int> p1 = make_shared<int>(a);cout << p1.use_count() << endl; // 1shared_ptr<int> p2(p1); // copycout << p1.use_count() << endl; // 2int *src_ptr = p1.get(); // 获取原始指针cout << *src_ptr << endl; // value: 99p1.reset(); // 初始化 或者 释放原来的管理cout << p2.use_count() << endl; // 1
}
6 new delete,malloc free 的区别
C++经典面试题 | malloc和new的区别?
C++经典面试题 | new/new[]和delete/delete[]的区别原理
【校招面试 之 C/C++】第16题 C++ new和delete的实现原理
(1)malloc和new都是在堆上开辟内存的
malloc只负责开辟内存,没有初始化功能,需要用户自己初始化;new不但开辟内存,还可以进行初始化,如new int(10);表示在堆上开辟了一个4字节的int整形内存,初始值是10,再如new int[10] ();表示在堆上开辟了一个包含10个整形元素的数组,初始值都为0。
(2)malloc是函数,开辟内存需要传入字节数,如malloc(100);表示在堆上开辟了100个字节的内存,返回void*,表示分配的堆内存的起始地址,因此malloc的返回值需要强转成指定类型的地址;new是运算符,开辟内存需要指定类型,返回指定类型的地址,因此不需要进行强转。
(3)malloc开辟内存失败返回NULL,new开辟内存失败抛出bad_alloc类型的异常,需要捕获异常才能判断内存开辟成功或失败,new运算符其实是operator new函数的调用,它底层调用的也是malloc来开辟内存的,new它比malloc多的就是初始化功能,对于类类型来说,所谓初始化,就是调用相应的构造函数。
(4)malloc开辟的内存永远是通过free来释放的;而new单个元素内存,用的是delete,如果**new[]数组,用的是delete[]**来释放内存的。
new = malloc + 构造函数
delete = free + 析构函数
malloc/free为C的标准库函数,函数原型
void* malloc(size_t size)//参数代表字节个数
void free(void* pointer)//参数代表内存地址
#include <iostream>
#include <malloc.h>using namespace std;int main() {int num1 = 99;// [1] 申请内存int *p1 = (int *)malloc(sizeof(int));p1 = &num1;cout << *p1 << endl;// [2] 释放内存free(p1);int *p = new int[10]; delete []p;return 0;
}
new、delete则为C++的操作运算符,它调用的分别为赋值运算符重载operator new()和operator delete()。
#include <iostream>using namespace std;int main() {int *p2 = new int(10);cout << *p2 << endl; // 10delete p2;return 0;
}
7 类的size,virtual 类的size
类型占用字节数
32位编译器
char : 1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节 // int32_t
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节 // int64_t
unsigned long: 4个字节
64位编译器
char : 1个字节
char*(即指针变量): 8个字节*****
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节****
long long: 8个字节
unsigned long: 8个字节****
类中成员内存对其(4字节的整数倍)
class Person1{};class Person2{public:int age; // int32_t 4byte
};class Person3{public:int age; // int32_t 4bytechar c; // 1byte
};class Person4{public:int age; // int32_t 4bytechar c1; // 1bytechar c2; // 1byte
};class Person5{public:char c1; // 1byteint age; // int32_t 4bytechar c2; // 1byte
};class Person6{public:int age; // int32_t 4bytevoid func1(){}void func2(){}void func3(){int tp = 99;}};void t_size(){// 1 1Person1 p1;cout << sizeof(Person1) << " " << sizeof(p1) << endl;// 4 4Person2 p2;cout << sizeof(Person2) << " " << sizeof(p2) << endl;// 8 8 介绍:char c本身为1字节, 4+1 为了内存对齐4字节 4+(1+3)=8Person3 p3;cout << sizeof(Person3) << " " << sizeof(p3) << endl;// 8 8 介绍:char c本身为1字节, 4+1+1 为了内存对齐4字节 4+(2+2)=8Person4 p4;cout << sizeof(Person4) << " " << sizeof(p4) << endl;// 12 12 介绍:char c本身为1字节, 1+4+1, 两端分别要内存对齐4字节 (1+3)+4+(1+3)=12Person5 p5;cout << sizeof(Person5) << " " << sizeof(p5) << endl;// 4 4 函数不占用内存大小Person6 p6;cout << sizeof(Person6) << " " << sizeof(p6) << endl;
}
虚函数对类大小的影响
class Vir1{};class Vir2{public:virtual void func1(){};
};class Vir3{public:virtual void func1(){};int age; // 4byte
};class Son1: public Vir1{};class Son2: public Vir2{};class Son3: public Vir3{};void t_size_vir(){// 1 1 空占 1byteVir1 v1;cout << sizeof(Vir1) << " " << sizeof(v1) << endl;// 4 4 虚基类占 4byte (MinGW32, 一个虚函数指针大小)Vir2 v2;cout << sizeof(Vir2) << " " << sizeof(v2) << endl;// 8 8 虚基类占 虚指针+int32_t -> 8byteVir3 v3;cout << sizeof(Vir3) << " " << sizeof(v3) << endl;// 1 1 空占 1byteSon1 s1;cout << sizeof(Son1) << " " << sizeof(s1) << endl;// 4 4(依据base)Son2 s2;cout << sizeof(Son2) << " " << sizeof(s2) << endl;// 8 8(依据base)Son3 s3;cout << sizeof(Son3) << " " << sizeof(s3) << endl;
}
多继承,基类同名虚函数
[1] 子类把两个父类的方法同时给覆盖掉
class A1{public:virtual void info()=0;A1(){ printf("A1::A1()\n"); }~A1(){ printf("A1::~A1()\n"); }
};class A2{public:virtual void info()=0;A2(){ printf("A2::A2()\n"); }~A2(){ printf("A2::~A2()\n"); }
};class ASon: public A1, public A2{public:ASon(){ printf("ASon::ASon()\n"); }~ASon(){ printf("ASon::~ASon()\n"); }void info() override {cout << "this is info func !" << endl;}
};
ASon a1;
a1.info(); // ok-------------------
A1::A1()
A2::A2()
ASon::ASon()
this is info func !
ASon::~ASon()
A2::~A2()
A1::~A1()
ASon *a2 = new ASon();
a2->info(); // ok
delete a2;-------------------
A1::A1()
A2::A2()
ASon::ASon()
this is info func !
ASon::~ASon()
A2::~A2()
A1::~A1()
A1 *a3 = new ASon();
a3->info(); // ok
delete a3;-------------------
A1::A1()
A2::A2()
ASon::ASon()
this is info func !
A1::~A1()
8 右值引用
参考博文
C++11 增加了一个新的类型,称为右值引用( R-value reference),标记为 &&
。
- 左值是指存储在内存中、有明确存储地址(
可取地址
)的数据 - 右值是指可以提供数据值的数据(
不可取地址
)
class Test
{public:Test(){cout << "Test::Test()" << endl;}~Test(){cout << "Test::~Test()" << endl;}
};Test getObj()
{return Test();
}int t_yinyong()
{int a1;
// int &&a2 = a1; // error a1是左值 int &&为右值引用 *不合法
// Test &t1 = getObj(); // error getObj() 是右值(将亡值) Test&为左值引用 *不合法Test &&t2 = getObj(); // OK getObj() 是右值(将亡值) Test &&是右值引用 合法const Test& t3 = getObj(); // OK 常量左值引用是一个万能引用类型,它可以接受左值、右值、常量左值和常量右值。return 0;
}
9 c/c++中struct、static的区别
1 struct在C和C++中的区别
2 static在C和C++中的用法和区别
10 拷贝构造
书写拷贝构造
class Cat{public:Cat(){} // 模认构造Cat(string _name, int _age): name(_name), age(_age){}~Cat(){} // 析构Cat(const Cat &obj): name(obj.name), age(obj.age){printf("Cat::Cat(const Cat &obj)");}string name;int age;
};void t_copy(){// Cat c1("xhh", 18);
// Cat c2(c1);Cat *c3 = new Cat("xhh", 18);Cat *c4 = new Cat(*c3);printf("name: %s age: %d", c3->name.c_str(), c3->age); // name: xhh age: 18
}
为什么拷贝构造需要传入引用
参数为引用,不为值传递,防止拷贝构造函数的无限递归,最终导致栈溢出
。
11 禁止构造函数、禁止动态分配方式、显示构造、隐式构造
禁止构造函数
设置构造函数为私有private或protected
class Cat{private:Cat(){} // 模认构造~Cat(){} // 析构};void t_copy(){Cat c1; // Cat c1 = Cat(); Error
}
禁止动态分配方式
重载操作符new和delete以及[]为私有private或protected
class Cat{public:Cat(){} // 模认构造~Cat(){} // 析构private:void *operator new(size_t size){return nullptr;} // 重载newvoid operator delete(void *ptr) {} // 重载deletevoid *operator new[](size_t size){return nullptr;} // 重载new[]void operator delete[](void *ptr) {} // 重载delete};void t_copy(){Cat *c1 = new Cat; // errordelete c1; // errorCat *c2 = new Cat[5]; // errordelete[] c2; // error}
只能动态分配类对象
- 采用静态成员函数去创建对象
- 对外隐藏默认构造和析构
- default创建函数的默认实现(只能对默认函数进行设置)
- delete禁用一些函数的使用(禁用内部默认的拷贝构造)
class Dog{protected:Dog() = default; // 模认构造 通过附加说明符’= default’,编译器将创建此函数的默认实现~Dog() = default; // 析构
// Dog(const Dog &obj) = delete; // delete(禁止一些函数的使用) 禁用拷贝构造函数public:static Dog* create() {return new Dog();}static void destroy(Dog *ptr) {delete ptr; // 不要用 delete this;}};void t_copy(){Dog *d1 = Dog::create();Dog::destroy(d1);
}
显示构造explicit、隐式构造implicit
explicit关键字的作用就是防止类构造函数的隐式自动转换
使用explicit关键字,确实是可以禁止隐式构造。
class Pig{public:Pig() = default; // 模认构造~Pig() = default; // 析构// explicit Pig(string _name): name(_name){}explicit Pig(int _age): age(_age){}explicit Pig(string _name, int _age): name(_name), age(_age){}string name;int age;
};void t_copy(){Pig p1(18); // [1] 显式构造Pig p2 = 3; // [2] 隐示构造 explicit -> errorPig p3("xhh", 18); // [1] 显式构造Pig p4 = {"mcy", 3}; // [2] 隐式构造,初始化参数列表,C++11之前的版本不能通过,C++11新特性 explicit -> error
}
12 线程安全变量及关键字synchronized、volatile
synchronized
对方法(func)进行加锁
volatile
当变量被定义成volatile类型的时候,它就不会被编译器优化,在每次访问变量的时候都将重新在内存中读取它的值。
原子类型
std::atomic<T> t;
13 C++空类编译器自动生成的6个成员函数
对于空类,编译器不会生成任何的成员函数,只会生成1个字节
的占位符。
有时可能会以为编译器会为空类生成默认构造函数等,事实上是不会的,编译器只会在需要的时候生成6个成员函数:一个缺省的构造函数、一个拷贝构造函数、一个析构函数、一个赋值运算符、一对取址运算符和一个this指针。
缺省的6个函数
class Empty
{public:Empty(); //缺省构造函数Empty(const Empty &rhs); //拷贝构造函数~Empty(); //析构函数 Empty& operator=(const Empty &rhs); //赋值运算符Empty* operator&(); //取址运算符const Empty* operator&() const; //取址运算符(const版本)
};
使用函数
Empty *e = new Empty(); //缺省构造函数
delete e; //析构函数
Empty e1; //缺省构造函数
Empty e2(e1); //拷贝构造函数
e2 = e1; //赋值运算符
Empty *pe1 = &e1; //取址运算符(非const)
const Empty *pe2 = &e2; //取址运算符(const)
内敛函数的实现
inline Empty::Empty() //缺省构造函数
{}
inline Empty::~Empty() //析构函数
{}
inline Empty *Empty::operator&() //取址运算符(非const)
{return this;
}
inline const Empty *Empty::operator&() const //取址运算符(const)
{return this;
}
inline Empty::Empty(const Empty &rhs) //拷贝构造函数
{//对类的非静态数据成员进行以"成员为单位"逐一拷贝构造//固定类型的对象拷贝构造是从源对象到目标对象的"逐位"拷贝
}inline Empty& Empty::operator=(const Empty &rhs) //赋值运算符
{//对类的非静态数据成员进行以"成员为单位"逐一赋值//固定类型的对象赋值是从源对象到目标对象的"逐位"赋值。
}
14 C++ 之xxxx_cast关键字的使用
C++提供了四个转换运算符
const_cast <new_type>(expression)
static_cast <new_type> (expression)
dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
1 const_cast <new_type> (expression)
onst_cast转换符是用来移除变量的const或volatile限定符。
void t_const_cast(){const int n1 = 99;const int *p1_n1 = &n1; // Ok
// int *p2_n1 = &n1; // [1] Error: invalid conversion from 'const int*' to 'int*'int *p3_n1 = const_cast<int *>(p1_n1); // [2] OK// “未定义行为(Undefined Behavior)”。所谓未定义,是说这个语句在标准C++中没有明确的规定,由编译器来决定如何处理。*p3_n1 = 88;cout << n1 << endl; // 改掉了,但是打印还是 99cout << *p1_n1 << endl; // 打印88cout << *p3_n1 << endl; // 打印88
}
- 那我们又为什么要去const呢?
(原因1):我们可能调用了一个参数不是const的函数,而我们要传进去的实际参数确是const的,但是我们知道这个函数是不会对参数做修改的。于是我们就需要使用const_cast去除const限定,以便函数能够接受这个实际参数。
void showInfo(string *str){cout << *str << endl;
}void input_info(){const string str = "mcy 3";
// showInfo(&str); // errorshowInfo(const_cast<string *>(&str)); // OK const string * -> string *
}
2 static_cast <new_type> (expression)
该运算符把expression转换为new_type类型,但没有运行时类型检查来保证转换的安全性。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
为什么需要static_cast强制转换?
- 情况1:void指针->其他类型指针
- 情况2:改变通常的标准转换
- 情况3:避免出现可能多种转换的歧义
3 dynamic_cast <new_type> (expression)
该运算符把expression转换成new_type类型的对象。new_type必须是类的指针、类的引用或者void *。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。(安全)
为什么需要dynamic_cast强制转换?简单的说,当无法使用virtual函数的时候。
使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。
class Animal{public:virtual void info()=0;
};class Cat: public Animal{public:virtual void info(){};
};class Dog: public Animal{public:virtual void info(){};
};void t_static_dynamic_cast(){// 上行转换Cat *c1 = new Cat; // 0x01Dog *d1 = new Dog; // 0x02Animal *a1 = static_cast<Animal *>(c1); // ok 0x01Animal *a2 = dynamic_cast<Animal *>(d1); // ok 0x02// 下行转换Animal *aa = new Cat; // 0x03Cat *cc1 = static_cast<Cat *>(aa); // ok 0x03Cat *cc2 = dynamic_cast<Cat *>(aa); // ok 0x03Dog *dd1 = static_cast<Dog *>(aa); // 0x03 没有类型检查,不安全Dog *dd2 = dynamic_cast<Dog *>(aa); // ok nullptrcout << " --- " << endl;
}
4 reinterpret_cast <new_type> (expression)
new_type
必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。
该运算符的用法比较多。
void t_reinterpret_cast(){int n1 = 99;int *ptr = nullptr;int getNum = 0;// [1] 整数转为指针ptr = reinterpret_cast<int *>(&n1);cout << ptr << " " << *ptr << endl; // 0x61fe84 99// [2] 指针转为整数getNum = reinterpret_cast<int>(ptr);cout << &getNum << " " << getNum << endl; // 0x61fe84(16进制) = 6422152 值有}
15 虚继承如何解决了菱形继承的二义性问题
C++菱形继承产生二义性产生的原因
在VS工具中使用
cl -d1 reportSingleClassLayoutSon main.cpp
菱形继承
虚继承
16
17
【面试总结】小灰灰求职进行曲(二)C++语言方向相关推荐
- 【面试总结】小灰灰求职进行曲(四)操作系统
文章目录 1 进程间通信方式 2 多线程的通讯和同步 3 Linux 多线程环境下 线程joinable状态和unjoinable状态 4 什么是协程 5 自旋锁和互斥锁的区别 6 内存泄漏检测工具 ...
- 【面试总结】小灰灰求职进行曲(三)TCP/IP方向
文章目录 1 select,poll,epoll区别 2 time_wait 状态?为什么是 2MSL 3 TCP协议-如何保证传输可靠性 4 UDP不可靠数据传输原理 5 计算机网络:负载均衡 6 ...
- 【面试总结】小灰灰求职进行曲(五)HR面试常见问题
- IT职场人生系列之十二:语言与技术I
本文是IT职场人生系列的第十二篇. 最近移动互联网很流行,很多人都在学习IOS.Android编程.这也引起一个入行.改行的潮流. 那么,作为新手.老手,应该怎样选择自己学习的语言和技术呢? 本人从早 ...
- 面试专家陈建军出二十一组面试题(转)
====================== 面试专家陈建军出二十一组面试题 编者按:所有转载请注明华图公务员培训学校及作者陈建军,否则华图有权追究其法律责任 (一)面试题组 一:1.领导委托负责组织 ...
- iOS开发面试知识整理 – OC基础 (二)
iOS | 面试知识整理 – OC基础 (二) 1.C和 OC 如何混编 xcode可以识别一下几种扩展名文件: .m文件,可以编写 OC语言 和 C 语言代码 .cpp: 只能识别C++ 或者C语言 ...
- 【华为笔试】安排面试官。有M个面试官,每个面试官熟悉的编程语言是一个列表,有N个面试者,按照面试者的机试选择的语言分配面试官进行面试(Python)
题目 有M个面试官,每个面试官熟悉的编程语言是一个列表,比如["Java", "C++", "Golang"]表示该面试官熟悉Java.C+ ...
- 2018年秋招笔试面试---小学渣求职历险记(深圳篇)
前文导读:2018年秋招笔试面试----小学渣求职历险记(中南篇) 我从来没有去过深圳,每次都是从男友那里了解到关于深圳的只言片语,说深圳开放,深圳包容,深圳如何如何-.反正我内心是不以为然的,眼见为 ...
- matlab语言实验二,实验二 MATLAB语言基础
实验二 MATLAB 语言基础 一.实验目的 基本掌握 MATLAB 向量.矩阵.数组的生成及其基本运算(区分数组运算和矩阵运算).常用的数学函数.了解字符串的操作. 二.实验内容 (1) 向量的生成 ...
最新文章
- SAP PM入门系列33 - IP16 维修计划报表
- 闪退没由报错_秉承工匠精神,3步定位飞桨报错原因,你也来试试?
- c语言n次方怎么输入_C语言实现斐波拉契数列
- 东网科技荣膺2016中国大数据最佳实践奖
- 2018年全球智能手机销售收入增至5220亿美元 但销量却下降了
- linux mysql查看所有表_linux下查看指定进程的所有连接信息
- 魔术师usm安装服务器系统,系统总裁开发的u盘魔术师安装win7图文教程
- multisim 10 小知识
- 如何模拟地震、噪音、颠簸路面激励下的不确定性振动行为?
- 模电、数电、电路面试题
- wegt安装tomcat镜像use --no-check-certificate
- 邮箱服务器 拦截策略,企业邮箱服务器的安全管理策略
- 通过阿里云或清华镜像站安装tensorflow2.0
- 大数据基础(林子雨版)
- springboot+poi开发excel导出 加载Excel模板导出 Excel批量导出详解
- 从JavaScript到TypeScript,Pt。 IIB:使用类,接口和混合器进行设计
- 最佳Outlook 2007改进-包括日历
- ios真机调试,iTunes检测得到,hbuilder未检测到手机和模拟器的解决办法
- 2022.05.18-使用Lightroom批量将图像从Raw转换为jpg
- python exercise string之一常规操作