应届生就业准备

来源:互联网 发布:360软件源 编辑:程序博客网 时间:2024/04/28 23:57

考研失败,只能着手找工作。下面是我在找工作期间一些的学习、准备笔记。主要摘自《程序员求职成功路——技术、求职技巧与软实力培养》

1、成员初始化方法

初始化方法有:初始化列表和函数体内两种。
内建型的如int float char 这类不区两种。非内建型别的初始化应该在初始化列表中实现,const 常量必须在初始化列表中实现。
内建类型有int ,float ....
看下面的代码:
class  myclass {
public:
       myclasss(const string &initval, char *initptr(;
private:
      const string val;
      char *const ptr;
}


myclass::myclass(const string &initval, char *initptr)
{
      val = initval;
      ptr = initptr;
}

由于这里string类型,实际上myclass 继承了string类。因此对与myclass类而言,string 对象val 的构造函数总是要在程序执行到myclass 的构造函数体之前就已经被调用了。调用string 哪个个构造函数是根据myclass 类的成员初始化列表。如果没有为val 指定初始化参数,则string 的缺省构造函数会被调用,当在myclass 的构造函数里对val 执行赋值是,会对val调用operate=函数,即总共对string 的成员函数两次调用:一次缺省构造函数,另一次赋值。

如果用一个成员初始化列表来指定val必须用initval 来初始化,val就会通过拷贝构造函数以仅有一个函数调用的代价被初始化。

myclass::myclass(const string &initval, char *initptr)
:val = initval, ptr = initptr
{
    ........
}

成员在构造函数中通过成员列表的方式进行初始化 的顺序应该与声明的顺序一致。


静态成员即static成员只能在类定义外初始化。
如static int x;
int 类名::x = 0;


2、虚析构函数
如果一个类将作为基类,那应该将其析构函数定义为虚函数,即虚析够函数。
class myclass {
//.....
virtual ~myclass() {}
};
这样将会避免这样的错误:
myclass *itemP = new myclass;
delete itemP; 
上面两行代码没有错误,释放指针前会调用析够函数释放内存资源。
但如果 baseclass 继承了myclass.
myclass *itemP = new baseclass; 即itemP 是指向派生类的指针
delete itemP; 这个时候就容易出错(如果基类的析够函数不是虚析够函数)


解决:通过将基类的析够函数声明为虚函数,虚函数性质都将继承。这里还有一个知识点就是 c++的函数调用默认不使用动态绑定,要触发动态绑定,必须满足两个条件:第一,只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数。第二,必须通过基类类型的引用或指针进行函数调用。


3、通过基类类型的引用或指针进行函数调用 (c++ 中的多态性)
因为基类类型的指针或引用可以用来引用派生类对象,所以使用基类类型的引用或指针时,不知道指针或引用所绑定的对象的类型。
虽然不知道对象具有哪种类型,编译器都将它当作基类类型对象。 将派生类作为基类对象是安全的,因为每个派生类对象都拥有基类子对象。
任何在基类对象上执行的操作都可以通过派生类对象使用。


4、可以在运行时确定virtual函数的调用。
非虚函数总是在编译时根据调用该函数的对象、引用或指针的类型而确定。

5、当构造函数有默认值并且在类外实现,将默认值声明在类外实现的构造函数的声明中。

6、有动态分配的对象,系统无法自动调用析够函数释放资源,必须使用delete 进行释放操作。

7、类的静态成员属于类,所有的该类的对象共享该数据成员。

8、子类继承的时候,必须继承父类的所有特征,但不继承构造函数和析够函数。。

9、基类、派生类、子对象 构造函数

基类对象在创建时,只调用基类构造,派生类对象在创建时,先调用基类构造,完成派生类对象中从基类继承来的数据成员的初始化。
再调用派生类的构造函数,完成派生类对象自己的数据成员的初始化。

派生类调用构造函数
派生类名(总形参表): 基类名(实参表){派生类构造函数的函数体}

构造函数是先调用基类的构造函数,然后是派生类的构造函数
析够函数则是先调用派生类的析够函数,然后是基类的析够函数。


派生类中调用公有成员采用就近原则:
如果派生类和基类有重名且参数相同时的公有成员,则默认调用派生类的。若一定要调用基类的公有成员,可以采用添加类的作用域的方法


子对象:
类A的对象是类B的对象的成员。
先调用子对象的构造函数,再执行子对象的初始化,再执行自己的构造函数,完成其他数据的可视化。


更复杂一些,如果一个派生类,其含有子对象,则构造函数调用顺序:
基类构造函数,子对象构造函数,派生类构造函数


10、
类的赋值运算符
返回的类型应为引用。
对含有动态分配内存的类,在实现该类时,应该自己独立实现一个赋值运算。


11、模板&&继承
如果要实现一个处理不同数据类型的队列,那么不同的数据类型显然不会影响队列的行为,可以使用模板。
如果要时下一个对象,来计算不同类型的图形面积,不同的类型的图形计算面积的方法不一样,影响了对象的行为,因此可以使用继承。
可以在基类中定义一个计算面积的虚函数,然后在派生类中针对不同的图形应用不同的面积计算方法。这里的派生类将不再唯一,准确的说
是有一个共同的基类,一组不同的派生类。


菱形继承
class A
{public:
       virtual void f();
       ......
}


class B : public A
{public:
       virtual void f();
       ......
}


class C : public A
{public:
       virtual void f();
       ......
}


class D : public B, public C
{
  public:
       void  g();
}

这个时候,由于B,C分别继承了A,  D又继承了B、C, 所以D中有两分A的数据成员。

class A
{public:
       virtual void f();
       ......
}

class B : virtual  public A
{public:
       virtual void f();
       ......
}


class C : virtual  public A
{public:
       virtual void f();
       ......
}

class D : public B, public C
{
  public:
       void  g();
}
将B  C 通过virtual方式继承A,则D中就只有一份关于A的数据成员了。


菱形继承的一个问题是二义性,
void D::g()
{
   f();
}
当D::g() 不知道是来自那个的f, 为了避免二义性,可以显示的指出 :      
void D::g()
{
    B::f()
}

12、什么是多态??
多态性是指:从相同的基类中继承而来的不同的类,由于对某些函数的实现做了部分修改,使得从这些基类派生出来的类所定义的对象
对于同一个消息会进行不同的操作。多态性是通过虚函数实现。
通过基类指针或引用来请求使用虚函数时,c++会在与对象关联的派生类中选择正确的改写过的函数。


13、重载&&重写:

重载:
尽管函数名是一样的,但在编译时,编译器会对函数名进行修饰:
int func(intx , int y);
int func(char x, int y);
修饰为:
@func_int4int4
@func_char1int4
所以仅有返回值不同是不可以的。

重写:
在继承关系中,如果派生类重新定义的一个方法,其名称、返回值类型及参数列表与基类中的某个方法的名称、返回值类型
及参数列表匹配,那么派生类的方法覆盖了基类的方法,叫作方法发改或方法重写。

重写的要求:
派生类的方法名称、参数签名和返回值类型必须与基类的方法名称、参数签名和返回值类型一致。
派生类的方法不能缩小基类方法的访问权限,只能扩大。
派生类方法不能抛出比基类方法更多的异常,派生类方法抛出的异常必须与基类相同或抛出的异常类为基类抛出异常类的派生类。
基类静态方法不可被覆盖为非静态方法。派生类可以定义与基类的静态方法相同的静态方法,以便派生类隐藏基类的静态方法。同样,派生类
定义的静态方法也必须满足方法覆盖的约束,方法名、方法的参数签名、返回类型一致。不能缩小基类的访问权限,不能抛出比基类方法更多的异常。
基类的非静态方法不能被派生类覆盖为静态方法。

不要重写一个非虚函数
当用基类指针指向派生类对象时,若调用虚函数,则指针具体调用什么函数,由其指向的对象类型决定。
如果调用的是普通函数,由于指针本身是指向基类的指针,所以将会调用基类中的函数。
正因为处于上面的原因,如果重写了一个不是虚函数的函数,当指针调用的时候,就会出现两个相同的函数在类里,具体将会调用哪个未知。


重写与重载的区别:
方法重载发生在同一个类的内部的方法内,而方法重写发生在继承关系同,派生类重写了基类的方法。
方法重载不要求方法的返回类型一致,而方法重写要求返回类型必须一致。
方法重载要求方法的参数签名必须不一致,而方法重写要求方法的参数签名必须一致。
方法重载对方法的访问权限和抛出异常没有限制,而方法重写对方法的访问权限和抛出异常有限制。


14、为什么使用c语言中的函数需要添加:extern "C"  
原因在于,不添加extern "C", 编译器就会对函数名进行修饰,这样的话将不会找到相应的函数体。因为定义的函数名与调用的将会不一致。

15、STL

序列容器: vector  list  deque
关联容器: set,  map
适配器: stack  queue  priority_queue  适配器可以看成由其他容器实现的容器。 适配器没有提供迭代器, 也不能同时插入或删除多个元素。

两种迭代器:
iterator 为读/写模式
const_iterator  只读模式

sort()
reverse()
find()
copy()


vector 底层为数组, 连续内存,支持[] 操作符,支持尾部操作
list底层为链表, 为非连续内存,不支持[] 操作符, 支持任意位置操作。
deque 底层数据结构为队列,支持头部和尾部操作,支持[] 操作符。
set 底层数据结构为红黑树,set内根据值自动排序,每个元素值只能出现一次,不允许重复。multiset则允许重复。都在<set> 文件下

map / multimap 底层为红黑树,map是键值对,每一个元素都有有一个键,是排序的基础,每个键只能出现一次,不允许重复。 
multimap 跟map相同,只不过允许重复键,可用来当作字典。
map/multimap  set/multiset都是关联容器。 set 与map 不同的地方在于: set 仅有key_type类型,它的value_tpe  也就是key_type; 而且set不提供下标操作。


16、写时拷贝
当多个对象具有相同的值的时候,有写时拷贝机制用来节约资源。


17、智能指针:动态分配对象以及当对象不再需要时自动执行清理,因此不用担心内存泄漏的问题。

智能指针:
auto_ptr<T>    ptr(new Y);
shared_ptr<T>   ptr(new X);

18、为什么++i 的效率要高于i++
对于非内建型别来说,前自增运算(++i)返回的是对象的引用,而后自增运算返回的是对象,返回对象将造成拷贝
构造函数更多的调用,所以++i 的效率要高于 i++.
对于内建的类型,++i ,i++ 效率没有区别。

19、如何禁止对象产生于堆中
将new 和delete 声明为private 权限,禁止被调用即可。
class myclass {
 public:
   .....
private:
      static   void *operator  new(size_t size);
      static   void  operator delete(void *ptr);
}
myclass *pDemo = new Demo;//这样是错误的,因为对new   delete重载且放到private下,导致无法使用new  delete


20、禁止对象产生非堆的对象
将析够函数声明为protected 或者private 权限。


21、默认构造函数 && 拷贝构造函数
系统默认生成缺省的构造函数、拷贝构造函数、赋值函数。
构造函数:与类同名且没有返回值,有形参表和函数体。可以重载。
如果不显示定义一个构造函数,编译器将自动为这个类生成默认构造函数。
内置类型成员的初值依赖与对象如何定义。如果对象在全局作用域定义,或为静态局部对象,初始化为0.在局部作用域定义,没有初始化,

拷贝构造函数,单个形参,且该形参是对本类类型对象的引用。

c++ 初始化:直接初始化( )和复制初始化 =
直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数,复制初始化首先使用指定构造函数创建一个临时对象,然后
用拷贝构造函数将那个临时对象复制到正在创建的对象。

系统默认的拷贝构造是基于位赋值,这也是它为什么容易出现问题的地方。
void DoNothing(String  localstring)
{}
String s = "Hello, my house!";//这里采用直接初始化, s内有指针指向"Hello, my house".
DoNothing(s);
当DoNothing时,由于String 类没有自己的拷贝构造函数,默认的拷贝构造函数将会被调用。也就是位拷贝。那么localstring 中也会有一个指针指向“Hello,my house” 且与s中的是一个地址。如果我们自己写一个构造函数,则会与s的"Hello, my house" 不在同一个地址空间。
当DoNothing 结束后,localstring 离开来其生存空间,调用析够函数,则将会出现 s 包含一个指向localstring 早已经删除的内存的指针。

这里有一个知识点:
系统为形参分配单元,然后调用形参对象的拷贝构造函数将实参对象初始化。如果没有对拷贝构造函数重载,就会调用系统默认的构造函数,将进行bit赋值。如果对象中有指针,则就会出现上面的问题。

原创粉丝点击