编程语言
Python
基本语句
- lamda函数
- 将lambda函数赋值给一个变量,通过这个变量间接调用该lambda函数
sum = lambda x,y : x+y
print(sum(1,2))
#等价于
def sum(x,y):
return x+y
print(sum(1,2))
- 作为参数传递给其他函数。部分Python内置函数接收函数作为参数
print(list(filter(lambda x: x%2,range(10)))) #[1, 3, 5, 7, 9]
#等价于
def odd(x):
return x%2
temp = range(10)#默认从0开始,含头不含尾
print(temp)
show = filter(odd,temp)#过滤序列 返回符合条件元素组成新列表
#筛选出序列为奇数的元素
print(list(show)) #[1, 3, 5, 7, 9]
- 作为其他函数的返回值,返回给调用者。
定义在某个函数内部的函数,称之为嵌套函数(内部函数)。将包含嵌套函数的函数称之为外部函数。内部函数能够访问外部函数的局部变量 例如return lambda x, y: x+y返回一个加法函数
- 赋值给其他函数,从而将其他函数用该lambda函数替换。
为了把标准库time中的函数sleep的功能屏蔽(Mock),我们可以在程序初始化时调用:time.sleep=lambda x:None。这样,在后续代码中调用time库的sleep函数将不会执行原有的功能。例如,执行time.sleep(3)时,程序不会休眠3秒钟,而是什么都不做
- 将if…else语句缩减为单一的条件表达式,expression1 if A else expression2(如果A为True,条件表达式的结果为expression1,否则为expression2)
s=lambda x:"yes" if x==1 else "no"
print(s(0))
print(s(1))
#等价于
def s(x):
if x==1:
return "yes"
else:
return "no"
print(s(0))
print(s(1))
面试题
OOM(Java)
Out of memory,内存不够或者耗尽,占用的内存加上我们申请的内存资源超过了Dalvik虚拟机(Google 公司自己设计用于Android平台的 虚拟机)的最大内存限制就会抛出。
在项目开发过程中经常遇到的就是BITMAP,在大图加载的时候会出现
怎么解决OOM?
大图优化:图片显示(图片尺寸合适,优先显示缩略图);及时释放内存(GC机制);图片压缩;inBitmap属性(可以提高安卓系统在bitmap分配和释放的执行效率);捕获异常(避免应用在分配bitmap内存中出现oom异常)
谨慎使用多线程
区分:
- 内存溢出 OOM:最严重,堆内存上有些内存没有被释放从而会失去控制造成程序使用的内存越来越少,导致系统运行速度减慢,严重情况下,oom他会造成整个程序的奔溃
- 内存抖动:在短时间内,大量的对象被创建,然后马上释放,瞬间产生的对象会严重占用内存区域.当达到一定程度,就会触发GC(垃圾回收),
- 内存泄漏:根本原因是GC垃圾回收机制漏掉的垃圾对象无法回收,从而导致该垃圾对象持有的内存持续占有,当内存泄漏累计到一定程度,就会造成内存溢出的现象
Python 深拷贝和浅拷贝概念理解
浅拷贝,指的是重新分配一块内存,创建一个新的对象,但里面的元素是原对象中各个子对象的引用。
深拷贝,是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联。
Python的数据类型
变量不需要声明,但是需要直接赋值
Number(数字):int、float、bool、complex(复数)
String(字符串):用’ ‘括起,要使反斜杠不发生转义,则加r,r’Ru\noob’
List(列表):写在方括号 [] 之间、用逗号分隔开的元素列表
Tuple(元组):写在小括号 () 里,元素之间用逗号隔开
Set(集合):使用大括号 { } 或者 set() 函数创建集合 {'b', 'c', 'a', 'r', 'd'}
Dictionary(字典):用 { } 标识,它是一个无序的 键(key) : 值(value) 的集合
{‘name’: ‘runoob’,‘code’:1, ‘site’: ‘www.runoob.com’}
列表和元组的区别
- 创建语法不同:[] ()
- 是否可变:可变,不可变
- 支持的方法:在列表当中可以使用的增删改方法函数在元祖当中均没有。因为元组不可变
- 存储结构与大小:列表占用的空间更大,性能低但是存储内容可变;元组占用空间更小,性能更高,但是存储内容不可变。
C++
基本语句
-
substr(str,pos,len):复制子字符串
返回值: string,包含s中从pos开始的len个字符的拷贝(pos的默认值是0,len的默认值是 s.size() - pos,即不加参数会默认拷贝整个s) 异常 :若pos的值超过了string的大小,则substr函数会抛出一个**out_of_range**异常;若pos+n的值超过了string的大小,则substr会调整n的值,只拷贝到string的末尾
**substr("sfsa",0,3)//sfs
substr("sfsa",2,3)//sa**
- vector数组向量
cin.get()=='\n’//判断输入字符是否是换行
vector.back()//获取v中的最后一个
vector<vector<int>> matrix(n,vector<int>(n));//创建大小固定的二维矩阵
r=matrix[0].size()-1//列数
b=matrix.size()-1//行数
//getline的用法 一行排序后的字符串,用','隔开 a,c,bb
while(getline(cin, s)){
stringstream ss(s);//输入输出流
while(getline(ss, s, ',')){
v.push_back(s);
}
- 升序排列与非递减顺序
1,2,3,4,5:递增排列,
1,2,3,3,4,5,8,8:非递减排列,数列递增,但不是单调递增,允许有重复
- 向上取整和向下取整
ceil(1.5)=2
floor(1.5)=1
- Unordered_set 经常用于无序、不可重复的场景
set.find(x)!=set.end()//从头到尾遍历有重复
set.count() #判断重复,对特定元素进行计数,若重复则返回1,否则返回0
- sizeof和strlen
char a[2][3] = {{'a', 'b', 'c'}, {'1', '\0', '2'}};
printf("%d", sizeof(a));
//strlen 计算字符串长度,遇到\0终止 为4
//sizeof计算物理空间大小 6
char a[] = "a0\0a0\0";
printf("%d, %d", sizeof(a), strlen(a)); 7 2
//\0算一个字符,数组结尾自动添加一个\0,所以共7个字符
计算与辨析
- ***和&**的用法
- 指针的声明:int *p 读法:p是指向一个整数类型的指针。
- 复合指针: int ***p读法 p是一个指向一个指向整数类型的指针的指针。(同样道理, intp等等)
- 解引用: x=*p 把指针p指向的值赋值给x
p=&x;读法:把x的地址赋给p(指针)
int& a=b,给b这块空间多加一个名称叫a
&a,取a的存储单元的地址
- *占用的字节
指针即为地址,跟系统的寻址能力有关,譬如以前是16为地址,指针即为2个字节,现在一般是32位系统,所以是4个字节,以后64位,则就为8个字节。
printf("%d\t%d\t%d\n", sizeof(char *), sizeof(int *), sizeof(float *));//在64位系统中,都为8
- 与或非
与(&):都为1才为1
或(|):只要有1就是1
非运算:~:1取0,0取1
异或运算(^):相异为1
*p=a和p=&a的区别
*P=a; 表示把a的值赋值给P所指的内存地址,PA本身没变。
P=&a; 表示指针P指向变量a的地址,PB本身变了。
#include <iostream>
using namespace std;
int main()
{
int a = 2;
int *p = &a;
printf("%d\n", *p); //值2
int *PA = new int;
cout << "PA 地址=" << PA << endl;//007064E8
*PA = a;
cout << "*PA=" << *PA << endl;//2
cout << "PA 地址=" << PA << endl; //地址不变 007064E8
cout << "------------我是分割线------------" << endl;
int *PB = NULL;
/*如果使用PB = &a后,对PB进行delete会报错,
因为PB已经指向a的地址,对a进行delete操作,是不行的。
但是不delete又会内存泄漏,所以用空指针指向a。
*/
//int *PB = new int;
cout << "PB 地址=" << PB << endl; //00000000
PB = &a;
cout << "*PB=" << *PB << endl;//2
cout << "PB 地址=" << PB << endl; //地址变了 0056FA10
delete PA; //记得用delete释放指针PA占用的内存
PA = NULL; //然后使指针指向空
getchar();
return 0;
}
指针常量与常量指针的区别
const char *P1=“hello“; char *const P2=“world“;下列操作合法的是
- P1++;
- P1[2]=‘W’;
- P2++;
- P2[2]=‘1’;
P1和P2都是字符指针,所以字符串"hello"和"world"都是字符串常量,不能修改,所以操作2 4都不合法。
P2是指针常量,也就是说P2的地址是不能修改的,操作3:P2++不合法。
P1是常量指针,指针指向的内容是常量,不能通过其修改,但是指针本身可以修改,P1++合法。
*常量指针 const char P1:,可以指向任何变量或常量,也可以更改指向的对象,但是不管指向谁,他都不能去修改那个指向对象的值。
应用场景:在函数参数传递时,往往需要传递指针(地址)或引用传递,通过地址修改数据是很容易的,为了防止函数修改数据,用const修饰指针,这就是常量指针。
int a[100]={0,1,2,3,4,5,6,7,8,9,10};
void find(const int *a){
cout<<a[0]<<endl;
cout<<a[5]<<endl;
//a[3]=10; 错误语句 不能修改
}
int main(){
find(a);
}//输出0 5
*指针常量 char const P2: 是一个常量,在声明的时候一定要赋初始值。将一个常量(常量的值是地址)与一个对象锁定,
应用场景:引用
int a;
int* const p = &a; //声明了一个常量p,它的值是变量a的地址
指针数组( * stings[] ):这个变量是一个数组,这个数组的所有元素都是指针类型,
数组指针( (*strings)[] ):这个变量是一个指针,这个指针指向一个数组的首地址。
char s[20]="programming",*ps=s ;
ps+2指针向后移动2个单位指向字符'o'的地址,并不是地址中的值。所以A错误。
s[2]采用数组下标的方式表示,从0开始的字符,刚好是'o'
ps[2]采用指针下标的方式表示,从0开始的字符,刚好是'o'
*(ps+2)的地址进行取存放的值,刚好是'o'
int A[2][3]={1,2,3,4,5,6}; *(*(A+1)+1)
//*(A+1)指向数组第1行第0个元素;*(A+1)+1指向数组第1行第1个元素;再取*则为第1行第1个元素的值5
一\二\三维指针数组
#include <stdio.h>
char *c[]={"ENTER","NEW","POINT","FIRST"};//一维指针*c,指向每个字符串的首地址
char **cp[]={c+3,c+2,c+1,c};//二维指针**cp,指向数组c首地址的偏移量c+n,c+n是存放第n+1个字符串地址的数组c第n+1元素的地址
char ***cpp=cp;//cpp就是存放字符串地址的数组c的数组元素地址
void main()
{
printf("%s\n\r",**++cpp);
printf("%s\n\r",*--*++cpp+3);
printf("%s\n\r",*cpp[-2]+3);
printf("%s\n\r",cpp[-1][-1]+1);
}
- ++cpp就相当于取&cp[1],那么 ++cpp == cp[1] == (c+2), 这是保存“POINT”字符串首地址的数组c的成员地址,于是*++cpp就是“POINT”字符串首地址打印为:“POINT”*
- - -++cpp+3, 根据第一步,其中*++cpp == cp[2] == &c[1], 于是*–*++cpp == *(–&c[1]) == *(&c[0]) == c[0], 而c[0]是字符串"ENTER"的首地址,所以 - -++cpp == c[0] +3 == “ER”, 打印为: “ER”
- 由于第二步后,指针cpp首地址指向&cp[2], 于是cpp[-2] = cp[0] == &c[3],FIRST
于是,*cpp[-2] +3 == c[3] + 3 == “ST”, 打印为:“ST” - 由于第二步后,指针cpp首地址指向&cp[2], 于是cpp[-1] == cp[1] == &c[2], 那么cpp[-1][-1] == c[1], 所以 cpp[-1][-1]+1 == c[1] +1 == “EW” , 打印为:“EW”
C++中的纯虚函数和虚函数的区别
相同:虚函数和纯虚函数都是为了多态这一特性而服务的。多态的实现建立在子类继承父类的基础上,在父类中声明为虚函数,而纯虚函数是将父类上升为一个抽象类,抽象类无法实例化,只有方法的声明,其实现由其子类完成。比如动物无法实例化为具体的对象,而老虎、狮子都可以继承动物的特性(即父类的接口),从而有自己的实例化对象。
不同:
1、类如果声明了虚函数,这个函数是实现了的,空实现(为了能让这个函数在它的子类里面可以被覆盖,这样编译器就可以使用动态绑定来达到多态的目的(即父类指针指向子类对象,调用子类方法))。而纯虚函数只是在基类中的一个函数声明,具体的实现需要留到子类当中。
2、虚函数在子类里面也可以不进行重写(只有虚方法和抽象方法才能够被重写);但纯虚函数必须在子类去实现。
3、虚函数的类用于“实作继承”,也就是说继承接口的同时也继承了父类的实现。纯虚函数的类用于“介面继承”,即纯虚函数关注的是接口的统一性,实现由子类去完成。
☆4、纯虚函数将父类上升为一个抽象类,无法实例化对象,只有接口声明,其实现由其子类完成,其基类不可实例化。只需要使用父类指针,指向具体的子类对象,去调用相应的虚方法即可。
#include <iostream>
using namespace std;
// 抽象类
class Base {
public:
virtual void func() = 0; // 纯虚函数
};
class child1 : public Base {
public:
void func() { cout << "it's child 1" << endl; } // 覆写父类的纯虚函数
};
class child2 : public Base {
public:
void func() { cout << "it's child 2" << endl; } // 覆写父类的纯虚函数
};
int main() {
// Base b; // 纯虚函数无法实例化
Base* b = nullptr; // 父类指针
child1 c1; // 子类对象
child2 c2; // 子类对象
b = &c1;
b->func(); // 多态
b = &c2;
b->func(); // 多态
return 0;
}
问答
设计一个不被继承的类(海康笔试)
要想不被继承,需要将析构函数和构造函数设置为私有的。但是两个函数被设置为私有后,这样一来这个类在其他地方不能实例化,没有存在的意义。如何解决不能继承的类不能实例化的问题?
- 通过静态方法来解决
以通过静态方法来返回类的实例,然后通过另一个静态方法来释放该类对象。
静态成员函数不属于类对象所有(属于类的一部分)可以创建一个类来调用静态成员函数
也可直接不创建一个类,Base::来调用静态成员函数,因为他的生命周期是全局存在的
class Base
{
public:
static Base* getInstance(int n)//获得实例
{
Base* pa;//这一步跳过
pa = new Base(); //执行到这一步会执行一个构造函数来构造一个Base
pa->_num = n;
return pa;
}
static void destructInstance(Base* pInstance)//释放实例
{
delete pInstance; //在这里会调用析构函数
pInstance = NULL;
}
private:
Base()
{
cout<<"Base::Base"<<endl;
}
~Base()
{
cout<<"Base::~Base"<<endl;
}
public:
int _num;
};
int main()
{
Base* pInstance = Base::getInstance(10);
cout<<pInstance->_num<<endl;
Base::destructInstance(pInstance);
return 0;
}
- 使用友源(堆和栈上都可以创建对象)
友源函数可以访问类的私有成员
class Base
{
public:
friend void test();
private:
Base(int x)
:_num(x)
{
cout<<"Base::Base"<<endl;
}
~Base()
{
cout<<"Base::~Base"<<endl;
}
public:
int _num;
};
class Derived:public Base
{
public:
int _a;
};
void test()
{
cout << "--------------object on heap-----------------" << endl;
Base *pfc = new Base(9); // 堆上的对象
cout << "num is: " << pfc->_num << endl;
delete pfc;
cout << "-------------Object on stack-----------------" << endl;
Base fc(10); // 栈上的对象
cout << "num is: " << fc._num << endl;
//Derived d;//这样仍然不可以,因为创建派生类仍然需要调用基类的构造函数
}
int main()
{
test();
return 0;
}
用多线程交替打印两个数组
数组1234与abcd,交替打印,结果为1a2b3c4d
#include <string>
#include <thread>
#include <mutex>
#include <iostream>
using namespace std;
std::mutex data_mutex;//互斥量声明,0表解锁,1表加锁;std声明类命名空间,纺织名字被重复使用
int flag = 0;
void printA(int* a, int size) {
for (int i = 0; i < size; ) {
data_mutex.lock();
if (flag == 0) {
cout << a[i] << endl;
flag = 1;
++i;
}
data_mutex.unlock();
}
}
void printB(char* b, int size) {
for (int i = 0; i < size; ) {
data_mutex.lock();
if (flag == 1) {
cout << b[i] << endl;
flag = 0;
++i;
}
data_mutex.unlock();
}
}
int main()
{
int a[4] = { 1, 2, 3, 4 };
char b[4] = { 'a', 'b', 'c', 'd' };
std::thread tA(&printA, a, 4);
std::thread tB(&printB, b, 4);
tA.join();//确保thread子线程执行完毕后才能执行下一个线程
tB.join();
return 0;
}