【面试】Ready GO
来源:互联网 发布:php网页源代码加密 编辑:程序博客网 时间:2024/05/18 07:38
一. C++基本知识:面向对象的特性、构造函数、析构函数、动态绑定; C++中对内存的使用管理。
基础知识: (数据结构和算法) (编程能力) (部分数学知识) (问题的分析和推理能力) (并发控制)
二. C++基础知识
1. 定义一个空的类型,里面没有任何成员变量和成员函数,该类型sizeof,得到结果是多少?
当我们声明该类型的示例的时候,必须在内存中占有一定空间,否则无法使用这些示例。调用构造函数和析构 函数只需要知道函数的地址即可,这些函数的地址只与类型相关,而与函数的示例无关。C++编译器一旦发现一个类型中有虚函数,就为该函数生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针
如果传入的参数不是引用而是实例,那么从形参到实参会调用一次复制构造函数,把参数声明为引用可以避免这样无谓的消耗。
2. 标准库string类型
字符串字面值与标准库string类型不是同一种类型
for (string::size_type i=0; i<str.size(); i++)
3. 标准库 vector类型
vector<int>::size_type
for(vector<int>::size_type i=0; i!=vec.size(); i++)
4. 迭代器(一种检查容器内元素并遍历元素的数据类型)
vector<int>::iterator iter = ivec.begin(); (由于end操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作)。
for (vector<int>::iterator iter=ivec.begin(); iter != ivec.end(); iter++)
*iter = 0;
5. 成员函数 const不能改变其所操作的数据成员,const必须同时出现在声明和定义中,若只出现在一处将出现编译错误。double avg_price() const
6. 类对象
class Sales_item {
public:
private:
std::string isbn;
}
Sales_item item; 定义一个一个新的类型,但没有进行存储分配
7. 从const成员函数返回*this (在普通的非const成员函数,this的类型是一个指向类类型的const指针,可以改变this所指向的值,但不能改变this所保存的地址)
8. 构造函数初始化式 (可以初始化const对象或引用类型的对象,但不能对他们赋值。 初始化const或引用类型数据的唯一机会是在构造函数初始化列表中)
class ConstRef {
public:
ConstRef(int li);
private:
int i;
const int ci;
int &ri;
};
9. static类成员 (static成员函数没有this形参,他可以直接访问所属类的static成员,但不能直接使用非static成员。static成员是与特定类相关的) static成员是类的组成部分但不是任何对象的组成部分,所以static成员函数不能被声明为const,static成员函数也不能被声明为虚函数
三. 重载操作符与转换
1. 保留字operator后接需定义的操作符符号。重载操作符必须具有一个类类型的操作数(用于内置类型的操作符,其含义不能改变)。 至少有一个类类型的或枚举类型的操作数
Sales_item operator+(const Sales_item &, const Sales_item &);
2. 类成员与非成员(大多数重载操作符可以作为普通非成员函数或类的成员函数)
ConstRef::ConstRef(int li): i(li), ci(i), ri(li) {}
3. 输出操作符<<的重载
ostream& operator <<(ostream& os, const Sales_item &object) { //第二个参数是一个引用以避免复制实参
out << s.isbn << "\t" << s.avg_price();
return out;
}
4. 输入操作符>>的重载
instream& operator >>(instream &in, Sales_item &s) {
double price;
in >> s.isbn >> s.units_sold >> price;
if (in) //只在使用读入数据之前检查一次即可
s.revenue = s.units_sold * price;
else
s = Sales_item(); //reset 错误恢复
}
5. 算术操作符和关系操作符
Sales_item operator(const Sales_item &lhs, const Sales_items &rhs) { //加法返回一个值,而不是一个引用
Sales_item ret(lhs);
ret += rhs;
return ret;
}
inline bool operator ==(const Sales_item &lhs, const Sales_item &rhs) {
return lhs.a == rhs.b && lhs.b == rhs.b;
}
inline bool operator !=(const Sales_item &lhs, const Sales_item &rhs) {
return !(lhs == rhs);
}
Sales_items &operator +=(const Sales_items &rhs) {
units_sold += rhs.units_sold;
return *this;
}
四. 面向对象编程
在c++中,通过基类的引用或指针调用虚函数时,发生动态绑定。在运行时确定,所指向对象的实际类型所定义。如果派生类没有重新定义某个虚函数,则使用基类中定义的版本。已定义的类才可以用作基类,如果声明但没有定义,则不能用作基类。
1. virtual 与其他成员函数
触发动态绑定: (1) 只有指定为虚函数的成员函数才能进行动态绑定 (2)必须通过基类类型的引用或指针进行函数调用
非虚函数总是在编译时根据调用函数的对象、引用或指针的类型二确定。
2. 公用、私有、和受保护的继承
去除个别成员
class Base{
public:
std::size_t size() const {return n;}
protected:
std::size_t n;
}
class Derived : private Base {
public:
using Base::size;
protected:
using Base::n;
}
3. 友元关心和继承
友元关系不能继承
4. 构造函数为什么不能是虚函数
本文的主题是构造函数不能是虚函数,首先这不需要你用脑子去记,因为当你写出来虚构造函数时,编译器是能检查出来的。本文的目的是为什么构造函数不能是虚函数。
首先,先看一段错误的代码,下面的代码是通不过编译阶段的。
1 class A{2 public:3 virtual A(){4 this->value = 0;5 }6 private:7 int value;8 };
为什么构造函数不能是虚函数呢?这里你需要知道一个概念,那就是虚函数表vtbl,每一个拥有虚成员函数的类都有一个指向虚函数表的指针。对象通过虚函数表里存储的虚函数地址来调用虚函数。
那虚函数表指针是什么时候初始化的呢?当然是构造函数。当我们通过new来创建一个对象的时候,第一步是申请需要的内存,第二步就是调用构造函数。试想,如果构造函数是虚函数,那必然需要通过vtbl来找到虚构造函数的入口地址,显然,我们申请的内存还没有做任何初始化,不可能有vtbl的。因此,构造函数不能是虚函数。
5. 如何限制一个类对象只在堆上分配或者只在栈上分配?
- 第一种说法//只在栈上class stackonly{private: void * operator new(size_t Size) { }};//只在堆上class heaponly{private: heaponly(){} ~heaponly(){}};
前者利用了c++的重载机制+访问控制机制。后者利用了c++的访问控制机制。
前者重载了new运算符,并设为私有,因此,当用 new stackonly;时编译器就会报错。
后者则将构造函数设为私有,因此,当你 heaponly h;时会自动调用构造函数,这时编译器也会报错。
这种技巧在c++中是很常用的比如设计模式中的单件模式。
6.
trdest.resize(strsrc.size()); // !!!必须预先设置一个大小与strsrc相同 transform(strsrc.begin(), strsrc.end(), strdest.begin(), to_upper); // 转换为大写
7. static
类的static成员变量属于该抽象类,需要在类定义时初始化,不可以在对象的构造函数中初始化。static成员函数不可以声明为const和virtual
1.静态成员变量从属于一个类而非某个具体的对象,它的值被该类的所有对象所共享。
2.public的静态成员,可以由类名(或对象名)直接通过 “.” 操作符引用。
先初始化 静态变量 然后运行静态构造函数
次序是static变量,static构造函数,最后是非static构造函数;可以看出,静态数据成员有以下特点:
- 对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;
- 静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。在Example 5中,语句int Myclass::Sum=0;是定义静态数据成员;
- 静态数据成员和普通数据成员一样遵从public,protected,private访问规则;
- 因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它;
- 静态数据成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式为:
<数据类型><类名>::<静态数据成员名>=<值> - 类的静态数据成员有两种访问形式:
<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>
如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员 ; - 静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了;
- 同全局变量相比,使用静态数据成员有两个优势:
- 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性;
- 可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;
与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部服务而不是为某一个类的具体对象服务。静态成员函数与静态数据成员一样,都是类的内部实现,属于类定义的一部分。普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。通常情况下,this是缺省的。如函数fn()实际上是this->fn()。但是与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数
关于静态成员函数,可以总结为以下几点:- 出现在类体外的函数定义不能指定关键字static;
- 静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;
- 非静态成员函数可以任意地访问静态成员函数和静态数据成员;
- 静态成员函数不能访问非静态成员函数和非静态数据成员;
- 由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长;
- 调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以直接使用如下格式:
<类名>::<静态成员函数名>(<参数表>)
调用类的静态成员函数。
内联函数的定义对编译器而言必须是可见的,以便编译器能够在调用点内联展开该函数的代码。此时,仅有函数原型是不够的。
内联函数一般与宏相比较,宏采用的是直接代入的方式,不进行类型检查。
指针和数组的区别
1. 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。
指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险
2. 为了节省内存,C/c++把常量字符串放到单独的一个内存区域,当几个指针赋值给相同的常量字符串时吗,它们实际上会指向相同的内存地址。但用常量内存初始化数组,情况有所不同。
char a[] = "abcdefg";---------------数组内容能修改(字符数组)
char *p = "abcdefg";-----------------内容不能修改(字符串常量
3. 计算内存容量
用运算符sizeof可以计算出数组的容量(字节数)
这是因为sizeof(p)得到的是一个指针变量的字节数,相当于sizeof(char*),而不是p所指的内存容量。C++/C语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。
指针引用区别: 声明一个引用必须同时初始化
1. 非空区别: 一个引用必须总是指向某些对象
2. 合法性; 使用引用不需要测试合法性
3. 可修改区别:
第七章 指针与引用
【问题一】///////////////////////////////////////////////////////
void swap(int *p, int *q)
{
int *temp;
*temp = *p; //不是指向而是拷贝
*p = *q;
*q = *temp;
} //int *temp 不分配内存,于是系统在拷贝时临时给了一个随机地址,且函数结束后不收回,造成内存泄露
【问题二】///////////////////////////////////////////////////////
voidGetMemory(char *p, int num) {
p = (char *)malloc(sizeof(char) * num);
}
int main() {
char *str = NULL;
GetMemory(str, 100);
}
*p实际上是主函数str的一个副本,编译器总是要为每个参数制作临时副本。于是P所指内存改变了,但是str没变。
每执行一次就会申请一块内存,申请不能释放造成内存泄露
void GetMemory(char **p, int num) { //right
*p = (char *)malloc(sizeof(char) *num);
}
char *GetMemory(char *p, int num) { //right
p = (char *)malloc(sizeof(char) *num);
return p;
}
[char *str char str[]]+++++++++++++++++++++++++++
char c[] = "aaaa"; //分配一个局部数组
char *c = "bbbb"; //分配一个全局数组
如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。
C++ new能完成动态内存分配和初始化工作
BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
创建型模式(CREATIONAL PATTERN)
在软件工程中,创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式
创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通
过以某种方式控制对象的创建来解决问题。
创建型模式由两个主导思想构成。一是将系统使用的具体类封装起来,二是隐藏这些具体类
的实例创建和结合的方式。包含如下pattern:
抽象工厂Abstract Factory 模式,提供一个创建相关或依赖对象的接口,而不指定对象的具
体类。
工厂方法Factory Method 模式,允许一个类的实例化推迟到子类中进行。
生成器Builder 模式,将一个复杂对象的创建与它的表示分离,使同样的创建过程可以创建
不同的表示。
延迟初始化模式,将对象的创建,某个值的计算,或者其他代价较高的过程推迟到它第一次
- 【面试】Ready GO
- Ready? Go!
- Ready Go!
- ready go...
- Ready Go!
- Ready for Beijing. Go! Go!
- Ready To Go!
- JICQ,Ready to go...
- 【GO】Ready To Work
- Ready, Set, Go - Getting started with Tuscany
- Ready, Set, Go - Getting started with Tuscany
- Ready? Go! 下篇:多核并起(转)
- 01.ios之路-ready go!
- Ready? Go! 上篇:大道至简(转)
- $(..).ready()
- $.ready()
- 读S计划:Ready &amp; Go! - 关于学习计划的实施
- 读S计划:Ready &amp; Go! - 关于学习计划的实施
- oracle 常用的语句
- Verilog HDL无符号数和有符号数运算
- miniGUI3.0.12移植到Hi3531
- 深克隆与浅克隆
- c 位域
- 【面试】Ready GO
- 《软件工程核心知识》--全书格局
- 备战互联网2014校招 求职攻略大汇总
- pat 1069
- 面向对象编程基础 五 抽象函数和抽象类
- php中include和require区别
- 通过AJAX和PHP,提交JQuery Mobile表单
- jQuery .tmpl(), .template()学习资料小结
- auto_ptr 非同寻常