曾经所遇到的C/C++与python的技术坑一二

一.python中去除列表list中重复元素的方法:
1.使用内置的set,将list作为set的构造函数构造一个set再转换为list即可(但是会改变元素的排序,同fromkey)
example:
(1) l1 = ['b','c','d','b','c','a','a']
   l2 = list(set(l1))
   print l2
(2) l1 = ['b','c','d','b','c','a','a']
l2 = {}.fromkeys(l1).keys()
   print l2

2.使用list的sort方法保留他们的排序
example:
l1 = ['b','c','d','b','c','a','a']
l2 = list(set(l1))
l2.sort(key=l1.index)
print l2

二.python中浅拷贝和深拷贝的区别
1.举例:

>>> L1 = [2,3,4]      #L1变量指向的是一个可变对象:列表  
>>> L2 = L1           #将L1值赋给L2后,两者共享引用同一个列表对象[1,2,3,4]  
>>> L1[0] = 200       #因为列表可变,改变L1中第一个元素的值  
>>> L1; L2            #改变后,L1,L2同时改变,因为对象本身值变了  
[200, 3, 4]  
[200, 3, 4]  


如果不想改变列表L2的值,有两种方法:切片 和 copy模块
可以 使用copy.copy(),它可以进行对象的浅复制(shallow copy),它复制了对象,但对于对象中的元素,×××依然使用引用×××.


(1)、使用切片[:]操作进行拷贝


(2)、使用工厂函数(如list/dir/set)等进行拷贝


(3)、copy.copy()


>>> L1 = [2,3,4]   
>>> L2 = L1  
>>> id(L1);id(L2)     #共享引用一个可变对象  
45811784L  
45811784L  
>>> L2 = L1[:]        #切片操作  
>>> id(L1);id(L2)     #切片后,对象就不一样了  
45811784L  
45806920L  
>>> L1[0] = 200  
>>> L1;L2             #L1发生改变,L2没有变化  
[200, 3, 4]  
[2,   3, 4]  


 切片技术应用于所有的序列,包括:列表、字符串、元祖 
   >>>但切片不能应用于字典。对字典只能使用D.copy()方法或D.deepcopy()方法.
   
   
三.python中装饰器
与传统编程习惯的从上往下执行方式相比较而言,像是在函数执行的流程中横向地插入了一段逻辑。
在特定的业务领域里,能减少大量重复代码。
面向切面编程还有相当多的术语,这里就不多做介绍,感兴趣的话可以去找找相关的资料。




四.python中内存管理机制
答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制
*一、对象的引用计数机制
python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。
引用计数增加的情况:
1,一个对象分配一个新名称
2,将其放入一个容器中(如列表、元组或字典)
引用计数减少的情况:
1,使用del语句对对象别名显示的销毁
2,引用超出作用域或被重新赋值
sys.getrefcount( )函数可以获得对象的当前引用计数
多数情况下,引用计数比你猜测得要大得多。对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。
*二、垃圾回收
1,当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。
2,当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。
*三、内存池机制
Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。
1,Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。
2,Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。
3,对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。
五.进程与线程的区别
进程是一个动态概念,一个进程可以包含多个线程,同一个进程的线程之间共享地址空间和其他资源,并且在不同
进程之间的切换时间要远大于同一进程的不同线程之间


六.如何用Python来进行查询和替换一个文本字符串?
答:可以使用re模块中的sub()函数或者subn()函数来进行查询和替换,
格式:sub(replacement, string[,count=0])(replacement是被替换成的文本,string是需要被替换的文本,count是一个可选参数,指最大被替换的数量)
>>> import re
>>>p=re.compile(‘blue|white|red’)
>>>print(p.sub(‘colour’,'blue socks and red shoes’))
colour socks and colourshoes
>>>print(p.sub(‘colour’,'blue socks and red shoes’,count=1))
colour socks and redshoes
subn()方法执行的效果跟sub()一样,不过它会返回一个二维数组,包括替换后的新的字符串和总共替换的数量


七.单引号,双引号,三引号的区别
答:单引号和双引号是等效的,如果要换行,需要符号(\),三引号则可以直接换行,并且可以包含注释


八.如何用Python来发送邮件?
 可以使用smtplib标准库。
 以下代码可以在支持SMTP监听器的服务器上执行。
import sys, smtplib
fromaddr =raw_input(“From: “)
toaddrs = raw_input(“To: “).split(‘,’)
print “Enter message, end with ^D:”
msg = ”
while 1:
line = sys.stdin.readline()
if not line:
break
msg = msg + line
# 发送邮件部分
server = smtplib.SMTP(‘localhost’)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()


九.什么是闭包?


简单说,闭包就是根据不同的配置信息得到不同的结果


再来看看专业的解释:闭包(Closure)是词法闭包(Lexical Closure)的简称,
是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。


十.在嵌套的函数中,如果希望在内部函数修改外部函数的局部变量,应该使用什么关键字?
nonlocal关键字
>>> def Fun1():
                x = 5
                def Fun2():
                        nonlocal x
                        x *= x
                        return x
                return Fun2()


>>> Fun1()
25


十一.进程间通信方式
无名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。
有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。


十二. def func(*args):
pass
如何修改使得它能接受传递至少一个参数


十三.python生成器有什么作用?为什么要用它,有什么好处


十四.x/y与x//y的区别
在python 2.x版本中存在两种除法运算,即所谓的true除法和floor除法。


当使用x/y形式进行除法运算时,如果x和y都是整形,那么运算时会对结果进行截取,取运算的整数部分(Truncating division,截断除法)
# Python 2.7.6 (default, Mar 22 2014, 22:59:56)
>>> 1/2
0
如果x和y中有一个是浮点数,那么会进行所谓的true除法
# Python 2.7.6
>>> 1/2.0
0.5
如果采用x//y的形式,那么这里采用的是所谓floor除法,即得到不大于结果的最大整数值,这个运算时与操作数无关的。
# Python 2.7.6
>>> 2//3
0
Python 3.x


在未来的python 3.0中,x/y将只执行true除法,而与操作数无关;x//y则执行floor除法。


十五.python中type类型判断和isinstance的区别
在游戏项目中,我们会在每个接口验证客户端传过来的参数类型,如果验证不通过,返回给客户端“参数错误”错误码。
这样做不但便于调试,而且增加健壮性。因为客户端是可以作弊的,不要轻易相信客户端传过来的参数。
验证类型用type函数,非常好用,比如
>>type('foo') == str
True
>>type(2.3) in (int,float)
True


既然有了type()来判断类型,为什么还有isinstance()呢?
一个明显的区别是在判断子类。
type()不会认为子类是一种父类类型。
isinstance()会认为子类是一种父类类型。
千言不如一码。
[python] view plain copy
class Foo(object):  
    pass  
  
class Bar(Foo):  
    pass  
  
print type(Foo()) == Foo  
print type(Bar()) == Foo  
print isinstance(Bar(),Foo)  
输出
True
False
True


需要注意的是,旧式类跟新式类的type()结果是不一样的。旧式类都是<type 'instance'>。
[python] view plain copy
class A:  
    pass  
  
class B:  
    pass  
  
class C(object):  
    pass  
  
print 'old style class',type(A())  
print 'old style class',type(B())  
print 'new style class',type(C())  
print type(A()) == type(B())  
输出
old style class <type 'instance'>
old style class <type 'instance'>
new style class <class '__main__.C'>
True


不存在说isinstance比type更好。只有哪个更适合需求。


十六、new\delete\malloc\free的区别
new delete 是运算符,malloc,free是函数


malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。


对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。


因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。


十七、函数指针与指针函数,为什么要有函数指针?
因为函数有一份就够了,不需要重复的两份浪费内存,一份函数可以被并发的执行


十八、malloc的机制,不同编译器实现是否不同,vc,gc,bc


十九、malloc与calloc的区别:
与malloc的区别:
calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。


二十、private public protected的区别
  作用域       当前类    同一package   子孙类     其他package
 
                public        √         √                      √           √
 
               protected   √          √                     √           ×
 
               friendly       √          √                     ×           ×
 
               private        √          ×                    ×           ×


二十一、const
const的用法,特别是用在函数后面




在普通的非 const成员函数中,this的类型是一个指向类类型的 const指针。可以改变this所指向的值,但不能改变 this所保存的地址。
在 const成员函数中,this的类型是一个指向 const类类型对象的 const指针。既不能改变 this所指向的对象,也不能改变 this所保存的地址。


 


关键字:Const,Const函数,Const变量,函数后面的Const


看到const关键字,C++程序员首先想到的可能是const常量。这可不是良好的条件反射。如果只知道用const定义常量,那么相当于把火药仅用于制作鞭炮。const更大的魅力是它可以修饰函数的参数、返回值,甚至函数的定义体。


const 是constant的缩写,“恒定不变”的意思。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。所以很多C++程序设计书籍建议:“Useconst whenever you need”。




     1.用const修饰函数的参数


如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加const修饰,否则该参数将失去输出功能。const只能修饰输入参数:


如果输入参数采用“指针传递”,那么加const修饰可以防止意外地改动该指针,起到保护作用。


例如StringCopy函数:


void StringCopy(char*strDestination, const char *strSource);


其中strSource是输入参数,strDestination是输出参数。给strSource加上const修饰后,如果函数体内的语句试图改动strSource的内容,编译器将指出错误。


如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const修饰。


例如不要将函数voidFunc1(int x) 写成voidFunc1(const int x)。同理不要将函数voidFunc2(A a) 写成voidFunc2(const A a)。其中A为用户自定义的数据类型。


对于非内部数据类型的参数而言,象voidFunc(A a) 这样声明的函数注定效率比较底。因为函数体内将产生A类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。


为了提高效率,可以将函数声明改为voidFunc(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。但是函数voidFunc(A &a) 存在一个缺点:


“引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为voidFunc(const A &a)。


以此类推,是否应将voidFunc(int x) 改写为voidFunc(const int&x),以便提高效率?完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。


问题是如此的缠绵,我只好将“const&”修饰输入参数的用法总结一下。


对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。例如将voidFunc(A a) 改为voidFunc(const A &a)。


对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如voidFunc(int x) 不应该改为voidFunc(const int &x)。




     2用const修饰函数的返回值


如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。例如函数
constchar * GetString(void);
如下语句将出现编译错误:
char*str = GetString();
正确的用法是
constchar *str =GetString();
如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。
例如不要把函数intGetInt(void) 写成constint GetInt(void)。
同理不要把函数AGetA(void) 写成constA GetA(void),其中A为用户自定义的数据类型。
如果返回值不是内部数据类型,将函数AGetA(void) 改写为constA &GetA(void)的确能提高效率。但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。
函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。


例如:
classA
{
A & operate = (const A &other); // 赋值函数
};
Aa, b, c; // a, b, c 为A的对象


a= b = c; // 正常的链式赋值
(a= b) = c; // 不正常的链式赋值,但合法
如果将赋值函数的返回值加const修饰,那么该返回值的内容不允许被改动。上例中,语句a= b = c 仍然正确,但是语句(a= b) = c 则是非法的。


3const 成员函数


任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。以下程序中,类stack的成员函数GetCount仅用于计数,从逻辑上讲GetCount应当为const函数。编译器将指出GetCount函数中的错误。
classStack
{
public:
void Push(int elem);
int Pop(void);
intGetCount(void) const; // const 成员函数
private:
intm_num;
int m_data[100];
};
int Stack::GetCount(void)const
{
++ m_num; // 编译错误,企图修改数据成员m_num
Pop();// 编译错误,企图调用非const函数
returnm_num;
}
const 成员函数的声明看起来怪怪的:const关键字只能放在函数声明的尾部,大概是因为其它地方都已经被占用了。
关于Const函数的几点规则:


a.const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.
b.const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的.
c.const成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查.
e.然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的


 


 


补充:


标题:const放在后面有什么意思?


--------------------------------------------------------------------------------
一个函数
AcGePoint3dstartPoint() const;
const放在后面跟前面有区别么


==>
准确的说const是修饰this指向的对象的
譬如,我们定义了
classA{
public:
f(int);
};
这里f函数其实有两个参数,第一个是A*const this, 另一个才是int类型的参数
如果我们不想f函数改变参数的值,可以把函数原型改为f(constint),但如果我们不允许f改变this指向的对象呢?因为this是隐含参数,const没法直接修饰它,就加在函数的后面了,表示this的类型是constA *constthis。
const修饰*this是本质,至于说“表示该成员函数不会修改类的数据。否则会编译报错”之类的说法只是一个现象,根源就是因为*this是const类型的
http://blog.csdn.net/gmstart/article/details/7046140




二十二、
1.非成员函数和静态成员函数不能有CV限定符
2.用成员方式重载操作符,不能改变参数的个数


二十三、delete p 和delete [] p的区别


delete []objects; // 正确的用法
delete objects;  // 错误的用法
后者相当于delete objects[0],漏掉了另外99 个对象
严格应该这样说:后者相当于仅调用了objects[0]的析构函数,漏掉了调用另外99 个对象的析构函数,并且在调用之后释放内存时导致异常(如果存在析构函数的话),如果对象无析构函数该语句与delete []objects相同


注:
1 测试环境vc6
2 不保证观点正确
3 欢迎指正
由new分配的一个数组空间,比如说 int *array=new int[50],当用delete释放这个空间时,用语句delete []array和delete array是否等价!


C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。 


关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。 


对于 (1),上面提供的程序a可以证明了 delete[] 和 delete 是等同的。 


**基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。对于 new 的单个对象,只能用 delete 不能用 delete[] 回收空间。


二十四、map unordered_map hash_map的区别
结果分析


运行效率方面:unordered_map最高,hash_map其次,而map效率最低


占用内存方面:hash_map内存占用最低,unordered_map其次,而map占用最高


二十五、静态绑定与动态绑定
class Shape
{
public:
    enum ShapeColor{Red, Green, Blue};                //形状颜色
 
    virtual void Draw(ShapeColor color = Red) const = 0;
};
 
class Circle : public Shape
{
public:
    virtual void Draw(ShapeColor color) const
    {
        cout << "I am Circle::Draw. ";
        cout << "My color = " << color << endl;
    }
};
 
class Rectangle : public Shape
{
public:
    virtual void Draw(ShapeColor color = Green) const            //缺省的参数值被更改了
    {
        cout << "I am Rectangle::Draw. ";
        cout << "My color = " << color << endl;
    }
};


我主要想说两个问题。


(1)当你下面这样调用时,请解释会发生什么情况。


Circle cr;            //(1) 编译不通过
cr.Draw();            
 
Shape *ps = &cr;    //(2)
ps->Draw();


没错,(1)通过对象调用而不指定参数是错误的,而(2)的结果是这样的:color = 0代表Red这你应该是知道的。




分析:通过对象调用是静态绑定,一定要指定参数值,因为静态绑定这个函数不从base class继承缺省参数值。动态绑定却可以从base class继承参数值。注意,这里我就不强调动态绑定和静态绑定的概念了,但下面这个一定是静态绑定:
(2)第二个我想说的问题,请解释下面的调用结果。


Shape* ps1 = new Rectangle;
ps1->Draw();
 
Shape* ps2 = new Circle;
ps2->Draw();


是这样令人可喜的结果:






你是说,你在Rectangle中已经将Draw的缺省值改为1(Green)了,怎么没效果?


分析:Rectangle::Draw的缺省参数值为GREEN,但ps2的静态类型为Shape*,所以此调用的缺省参数值来自Shape class。


如果你非要让Rectangle::Draw的参数有所改变,可以这样调用(提供参数):


Shape* ps4 = new Rectangle;
ps4->Draw(Shape::Green);
 
Shape* ps5 = new Circle;
ps5->Draw(Shape::Green);


这个问题是想提醒你:virtual函数是动态绑定,缺省参数值是静态绑定。所以,不应该重新定义这个缺省参数值。


二十六、
1. 在表达式中,char 和 short 类型的值,无论有符号还是无符号,都会自动转换成 int 或者 unsigned int(如果 short 的大小和 int 一样,unsigned short 的表示范围就大于 int,在这种情况下,unsigned short 被转换成 unsigned int)。因为它们被转换成表示范围更大搜索的类型,故而把这种转换称为“升级(promotion)”。
2. 按照从高到低的顺序给各种数据类型分等级,依次为:long double, double, float, unsigned long long, long long, unsigned long, long, unsigned int 和 int。这里有一个小小的例外,如果 long 和 int 大小相同,则 unsigned int 的等级应位于 long 之上。char 和 short 并没有出现于这个等级列表,是因为它们应该已经被升级成了 int 或者 unsigned int。
3. 在任何涉及两种数据类型的操作中,它们之间等级较低的类型会被转换成等级较高的类型。
4. 在赋值语句中,= 右边的值在赋予 = 左边的变量之前,首先要将右边的值的数据类型转换成左边变量的类型。也就是说,左边变量是什么数据类型,右边的值就要转换成什么数据类型的值。这个过程可能导致右边的值的类型升级,也可能导致其类型降级(demotion)。所谓“降级”,是指等级较高的类型被转换成等级较低的类型。
5. 作为参数传递给函数时,char 和 short 会被转换成 int,float 会被转换成 double。使用函数原型可以避免这种自动升级。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值