结语
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是目录截图:
由于整个文档比较全面,内容比较多,篇幅不允许,下面以截图方式展示 。
再附一部分Android架构面试视频讲解:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
system("pause");
}
派生类继承的内容: 非private的非虚函数,虚函数,静态变量和函数。注意派生类和基类的同名静态变量共用一块内存。如果派生类不对基类函数隐藏,
那么就可以使用派生类对象直接调用基类的函数。
4. 多态性
一种接口有多种实现。编译时多态,由重载体现,相同函数名不同参数列表;运行时多态,由虚函数体现。
非虚函数的地址是编译期间就确定,虚函数地址运行期间绑定。会根据基类指针指向的对象得到虚表指针,然后根据虚表指针访问类的虚函数表,从而获得虚函数的地址,然后调用执行。
虚表的构建与需函数的调用过程参考:https://blog.csdn.net/Xiongchao99/article/details/73381280
所以调用非虚函数看变量类型,调用虚函数看对象。例如,
#include
using namespace std;
class A
{
public:
virtual void x()
{
cout << "A::x" << endl;
}
void y()
{
x();
cout << "A::y" << endl;
}
};
class B : public A
{
public:
virtual void x()
{
cout << "B::x" << endl;
}
virtual void y()
{
cout << "B::y" << endl;
}
};
int main()
{
A *p = new B;
p->y();
return 0;
}
结果:
B::x
A::y
虽然y()在B中是虚函数但在A中不是,也就是说只有调用B子类的该虚函数才会体现他的多态性。
这里调用y非虚函数,根据变量类型会调用A中的y(),然后因为x()是虚函数,根据对象类型A调用A中x();
如果在父类中是虚函数,那么子类不用加virtual也是虚函数。如果只在子类才是虚函数,那么从子类的子类开始才会体现多态。
普通函数为什么比虚函数调用快?
因为普通函数是静态联编的,而虚函数地址是动态联编的。
静态联编 :在编译的时候就确定了函数的地址,然后call就调用了。
动态联编 : 首先需要取到对象的首地址,然后再解引用取到虚函数表的首地址后,再加上偏移量才能找到要调的虚函数,然后call调用。
为什么子类和父类的函数名不一样,还可以构成重写呢?
因为编译器对析构函数的名字做了特殊处理,在内部函数名是一样的。
5. 浅拷贝和深拷贝
发生拷贝的情况:
(1)对象以值传递的方式传入函数体 (2)对象以值传递的方式从函数返回 (3)对象通过另外一个对象初始化或赋值。
浅拷贝只是拷贝了类成员的值。当对象中含所有指针变量时,浅拷贝只拷贝了指向同一块内存区域的指针,当其中一个对象对这块内存内存进行改变另一个对象也会受到影响,当一个对象释放掉这块内存,另一个对象的指针变量就会变成野指针,容易发生错误。而深拷贝会定义拷贝构造函数和赋值运算符重载,重新拷贝一份内存,是两个对象之间不会产生影响。
6. 纯虚函数
定义时虚函数后加=0。有纯虚函数的类是抽象类,不能生成对象。纯虚函数用于不方便生成对象的类,把纯虚函数交给子类实现。
7. const
const修饰变量:表示只读变量,根据初始化判断编译期常量还是运行期常量。
const修饰指针变量(const T\*p情况),最好是const指针给const指针赋值,非const指针给非const赋值。
//非const赋给const,const失去意义。
int i=10;
const int* p = &i;
cout << *ii << endl;//10
i = 20;
cout << *ii << endl;//20
//const赋给非const,语法错误
const修饰函数返回值:避免给表达式赋值(参考返回值引用)
const修饰成员函数:不允许在函数内部修改对象,const不能修饰static成员函数,因为const是为了保证对象不被修改,而static函数无对象。
8. static与全局变量
全局变量:定义在函数体外的变量(函数同理)就是全局变量,具有全局的作用域和生命周期。其他源文件想使用某个源文件中定义的全局变量,只需要extern 类型名 变量;声明即可。
静态变量:存储于全局/静态存储区,生命周期与程序相同,但作用域由定义位置决定,所以可以用static修饰全局变量,将全局变量作用域限定在当前源文件中。
static修饰类成员时,static属于类,类和派生类的所有对象都可以访问static成员变量,当然是在作用域允许的情况下(protected,public)。static成员变量必须在类外初始化,static成员函数可通过类名直接调用。
class A
{
public:
static void func() { cout << "stattuic"; }
const static int var;
static int var1;
};
const int A::var = 1;
int A::var1 = 1;
class B :public A {
};
int main()
{
A a;
B b;
cout << b.var << endl;
cout << b.var1 << endl;
A::func();
return 0;
}
由此可见静态变量和全局变量生命周期相同,但是static的作用域和普通变量是一样的。
8. 32位,64位系统中,各种常用内置数据类型占用的字节数?
char :1个字节(固定)
(*即指针变量): 4个字节(32位机的寻址空间是4个字节。同理64位编译器8字节)(随系统变化)
short int : 2个字节(固定)
int: 4个字节(固定)
unsigned int : 4个字节(固定)
float: 4个字节(固定)
double: 8个字节(固定)
long: 4个字节,64位8个字节(随系统变化)
unsigned long: 4个字节,64位8个字节(随系统变化)
long long: 8个字节(固定)
64位操作系统
char :1个字节(固定)
*(即指针变量): 8个字节
short int : 2个字节(固定)
int: 4个字节(固定)
unsigned int : 4个字节(固定)
float: 4个字节(固定)
double: 8个字节(固定)
long: 8个字节
unsigned long: 8个字节(变化*其实就是寻址控件的地址长度数值)
long long: 8个字节(固定)
除*与long 不同其余均相同。
9. C++类中数据成员初始化顺序?
1.成员变量在使用初始化列表初始化时,与构造函数中初始化成员列表的顺序无关,只与定义成员变量的顺序有关。
2.如果不使用初始化列表初始化,在构造函数内初始化时,此时与成员变量在构造函数中的位置有关。
3.类中const成员常量必须在构造函数初始化列表中初始化。
4.类中static成员变量,只能在类内外初始化(同一类的所有实例共享静态成员变量)。
初始化顺序:
1) 基类的静态变量或全局变量
2) 派生类的静态变量或全局变量
3) 基类的成员变量
4) 派生类的成员变量
const成员变量,引用成员变量, 其他类的对象必须在初始化列表中初始化。
10. static\_cast, dynamic\_cast, const\_cast, reinpreter\_cast的区别
static\_cast相当于c语言的强制类型转换.
dynamic\_cast要求转换类型必须是指针或引用,而且基类一定要包含虚函数。在上行转换(子类对象转换为基类类型)中和static\_cast效果是一样的,下行转换时要把父类类型转换为子类类型,这时父类指针一定要指向子类对象转换才是安全的。如果使用static\_cast不会进行安全性检查,dynamic\_cast会安全性检查,如果父类指针没有指向子类对象则返回null指针。
class A
{
public:
virtual void f() { cout << "A::f"; }
};
class B :public A {
public:
void f() {
cout << "B::";
}
int a;
};
int main()
{
//上行转换,安全
B* b = new B;
A* a = static_cast<A*>(b);
a->f();//satic_cast和dynamic——cast效果一样,都输出B::f
//下行转换,安全
A* a = new B;
B* b = static_cast<B*>(a);//或者dynamic_cast
cout << b->a;
a->f();
//下行转换,不安全
A* a = new A;
B* b = static_cast<B*>(a);
cout << b->a; //可能访问到不确定,也可能无效内存,反正不是b成员变量的值,只是相对b对象内存偏移一定位置内存的值。
a->f();
B* b = dynamic_cast<B*>(a);//返回nullptr
return 0;
}
reinterpret\_cast可以对无关类指针进行转换,甚至可以直接将整型值转成指针,这种转换是底层的,有较强的平台依赖性,可移植性差;
const\_cast可以将常量转成非常量,但不会破坏原常量的const属性,只是返回一个去掉const的变量。
11. 定义一个空类编译器做了哪些操作
一个空的class在C++编译器处理过后就不再为空,编译器会自动地为我们声明一些member function,一般编译过就相当于:
学习福利
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
[外链图片转存中…(img-Wv7wAnfv-1715621240629)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!