C++的一些基础细节(备考用)

class node
{
public:
node(){cout
<<"constructor node()"<<endl;}
~node(){cout<<"destructor ~node()"<<endl;}
};
class nn
{
public:
node x;
nn(){cout
<<"con nn()"<<endl;}
~nn(){cout<<"des ~nn()"<<endl;}
};
int main()
{
nn a;

return 0;
}

 

  几年前的一份C++笔记。

  看完了Bruce Eckel的Thing in C++(Volume 1),实践比较少,感觉还是半懂不懂,马上就要【面向对象程序设计】的考试了。考的是C++的东西,做了几份C++的卷子,感觉好多细节的东西还是很不确 定,不敢下手。于是今天把老师的课件翻了出来,整理了一下,做了个小结。虽然感觉很乱,但是对于我自己来说,还是能看懂,很多东西都很显而易见的也写了, 无论怎样,只是为了考试^_^


extern int xx 这是一个声明,而不是定义

在class temp中只定义一个static int b;

1 sizeof(temp) = 1

这是为什么?
static 变量是这个类共享的,变量是放在全局变量区,不是堆栈里,因此sizeof出来不算在这个static int的大小;是这样的吗??


枚举类型的变量只能进行赋值运算


指针int *p,后不能直接进行*p = 12这样的赋值,因为指针p没有申请内存


如何解释以下程序?

1 int main()
2 {
3 int *a;
4 
5 = (int *)"string";
6 cout<<*a<<endl;
7 cout<<a<<endl;
8 return 0;
9 }

a指向一个string常量的,强制转换为int *,因此a还是"string"常量的首地址,*a应该是取前面几位的值


析构函数的调用不一定要大括号

class node
{
public:
  node(){cout<<"node()"<<endl;}
  ~node(){cout<<"~node()"<<endl;}
};
int main()
{
  if(1)
  
  node n;
  cout<<"out of if"<<endl;

  return 0;
}

输出为
node()
~node()
out of if
另外,一个大括号就是一个scope,不管有没有if,while等语句


if(!cin)
相当于if(cin.fail())
!的运算符在这里被重载了


函数中的static变量在结束main()的时候才释放,但是在全局变量之前


全局变量区:全局变量,静态全局变量,静态本地变量
stack:本地变量
heap:申请的变量,例如,new,malloc
要注意的是new和malloc不能混用


class 中的public表示所有的object都能访问
private只能让类声明的函数和friend函数访问
protected可以子类访问,也可以被friend函数访问


无论是数组还是指针,传到函数中都变为指针


只有在没有构造函数的情况下,编译器才自动分配一个构造函数,否则,只要
编译器看到一个构造函数,就不会生成一个构造函数。
另外,编译器自动生成的构造函数是没有参数的。


根据函数的参数的个数或者类型的不同可以重载函数。
如果只是返回值不同,则不能重载。


缺省参数的函数。缺省值只能从后往前写。

也就是说int f(int i , int j = 0);是可以的。
但是int f(int i = 0 ; int j);是不对的。


一个class中如果有const 常量,声明的时候不能初始化,否则编译器报错

但是如果是static const int a = 0就可以
class中的const int a可以在构造函数中初始化,注意只能在初始化列表中,不能在函数体中赋值
其他的地方定义const 则需要初始化
如果是extern const int a ;就可以不做初始化


内联函数应该放在.h的文件中,因为它是一个声明
另外在class中定义的函数是内联的


1 const int a = 10;
2 int y = a;

是可以的


1 const int size = 10;
2 int a[size];

是正确的。
但是

1 int x = 10;
2 const int size = x;
3 double classAverage[size];

会报错(为什么?)


1 const int size[3= {2,3,4};
2 int a[size[0]];

是错的。
也不能这样定义

1 struct S { int i, j; };
2 const S s[] = { { 12 }, { 34 } };
3 double d[s[1].j];

1 char * const p = "abc";

表示p指向的地址是常量,指向的地址不能改变,但是指向的内容可以变

1 char const *= "abc";

表示p指向的内容是常量,不能改变,但是指向的地址可以改变


 1 int *ip;
 2 const int *cip ; //指针可以不初始化
 3 const int t = 3;
 4 int i;
 5 ip = &i ;
 6 ip = &t ; //错误,非常量指针不能指向常量
 7 cip = &i;
 8 cip = &t;
 9 *ip = 54;
10 *cip = 54;//错误


1 char * s = "hello , world";

是可以的,因为编译器自动转化为

1 const char * s = "hello,world";

因此不能再改变s的值了。 但是s指向的地址是可以改变的


void f(const int x){}
void g(int x){}
int a = 0;
const int b = 0;
f(a);
g(b);

都是可以的


如果返回值是const,可以赋给non-const,如果不是const 也可以赋给const

如果一个对象定义的时候是const,不能修改这个对象的值

int node::f()const

函数中不能修改成员变量
也不能调用非常量函数

int node::f(){}
int node::g()const{f();}

是错的。


不能这样定义

class node
{
private:
    
const int size;
    
int a[size];
};

可以把const int 改为static const int
或者用enum来做


引用不能改变

int y , z;
int &= y;
&= z;//错误,引用不能改变

int f(int &){}

不能调用f(t * 3);


不能这样定义

int&* p;

但是可以这样

int*& p;

引用变量的构造函数中也只能初始化,而不能赋值


如果函数的返回值是const的,就不能做左值


在node a = b;或者node a(b);拷贝构造调用。
当函数返回一个对象的时候,拷贝构造被调用

node f()
{
    node x;
    
return x;
}

在函数的参数中有对象的时候,调用拷贝构造

int f(node x)
{
    
return 1;
}

但是如果参数的引用,拷贝构造不被调用

int f(const node&)
{
    
return 1;
}


静态函数只能对静态变量进行操作



函数中的静态变量只能被初始化一次



static 成员变量在同类的对象中都可见



不能用this来取值



namesapce
可以用using nm::f();

void main() {
using MyLib::foo;
using MyLib::Cat;
foo();
Cat c;
c.Meow();
}




namespace my1
{
int f(){cout<<"my1::f()"<<endl;return 0;}
int g(){cout<<"my1::g()"<<endl;return 0;}
}
namespace my2
{
int f(){cout<<"my2::f()"<<endl;return 0;}
int h(){cout<<"my2::h()"<<endl;return 0;}
}
int main()
{
using namespace my1;
using namespace my2;
f();
g();
h();

return 0;
}

编译器报错,因为编译器不知道应该调用哪一个f(),
如果不调用f(),编译器就不会报错了。
但是如果将f()变为my1::f()就可以了
注意namespace后面没有";"



【运算符重载】



可以重载的运算符:
+     -     *     /     %     ^     &     |     ~
=     <     >     +=    -=    *=    /=    %=
^=    &=    |=    <<    >>    >>=   <<=   ==
!=    <=    >=    !     &&    ||    ++    --
,     ->*   ->    ()    []   
operator new        operator delete
operator new[]      operator delete[]



不能被重载的运算符
.      .*      ::      ?:
sizeof    typeid
static_cast    dynamic_cast
const_cast     renterpret_cast
另外不能重载不存在的运算符,例如**



运算符重载后的优先级不变
所接受的参数的个数也不变



重载为成员函数,可以省略第一个参数,但是
z = x + y;//可以
z = x + 3;//可以
z = 3 + y;//不可以
因为只看第一个参数
但是如果重载为全局函数,以上三个表达式都正确



必须是member function:
= () [] -> ->*
还有一元的运算符必须是member function
所有的二元运算符可以是non-member fuction



函数原型:
+    -   *    /    %    ^    &    |    ~
const T operatorX(const T& l, const T& r);
!    &&   ||    <    <=    ==    >=    >
bool operatorX(const T& l, const T& r);
[]
T& T::operator[](int index);



++ -- 运算符的重载

const Integer& operator++(); //++a
const Integer operator++(int); //a++
const Integer& operator--(); //--a
const Integer operator--(int); //a--

在后缀++重载的时候,应该先将原来的对象保存下来用来返回
在调用a++的时候,实际上调用的是a.operator++(0)
//前缀比后缀更加高效



关系运算符的重载

bool operator==const Integer& rhs ) const;
bool operator!=const Integer& rhs ) const;
bool operator<const Integer& rhs ) const;
bool operator>const Integer& rhs ) const;
bool operator<=const Integer& rhs ) const;
bool operator>=const Integer& rhs ) const;


【继承】



输出:
constructor node()
con nn()
des ~nn()
destructor ~node()



在构造函数调用的时候,总是基类先被调用
而析构函数的调用与构造函数的调用是反过来的



如果在子类定义了一个函数与父类的名字相同的话,父类中所有同名的函数被屏蔽



继承后
构造函数,析构函数,operator=,private不能被继承
子类不能访问private的变量,但是可以访问protected的变量



在继承中
class base:derived默认为private继承



class derived:public base
base的public在derived是public,protected在derived是protected,private在derived中被 隐藏
class derived:protected base
base的public在derived是protected,protected在derived是protected,private在 derived中被隐藏
class derived:private base
base的public在derived是private,protected在derived是private,private在derived中被隐 藏



upcasting
base &b = pete;//pete是子类,base是父类
b->f();
调用的是父类的f();
但是如果父类的f()是virtual的话,调用的是子类的f()



void func(Ellipse& elly) {
elly.render();
}
Circle circ(60F);
func(circ);

调用的是Circle::render();
但是如果

void func(Ellipse elly) {
elly.render();
}
Circle circ(60F);
func(circ);

调用的是Ellipse::render();



析构函数也可以virtual,作用与前面一样

base *= new derived;
delete p;

如果析构函数是virtual的,调用的是derived的析构函数,同时也会调用base的
否则调用的只是base的



overriding
可以这样定义:
在base中:virtual base& f();
在derived中:virtual base& f();
但是不能这样定义
在base中:virtual base f();
在derived中:derived f();



当override一个函数的时候,最好重载所有的函数



在构造函数中调用的virtual function是本身的f()



template<class T>
void swap(T& a , T& b)
{
    T temp;
    temp 
= a;
    a 
= b;
    b 
= temp ;
}

不能这样调用

int a;
double b;
swap(a,b);


template<class T>
void foo(){/**********/}
foo
<int> //此处的T为int




类的模版
template<class T>
class node.....
创建对象的时候,可以
node<int>...
如果函数不是内联的,在函数的定义的时候,前面要加上
template<class T>



template<class T , class U>
template<vector<class T> >注意后面是有空格的



也可以是变量
template<class T , int size>
可有带有缺省参数
template<class T , int size = 100>



模版可以用在继承上
template <class A>
class Derived : public List<A> {...};



模版应该放在.h的文件中



【异常】



try{}catch(){}catch{}



catch所有的异常
catch(...);



关于new的异常处理

void func() {
try {
    
while(1)
    {
        
char *= new char[10000];
    }
    } 
catch (bad_alloc& e)
      {
      }
}




还有一些stream的东西还没看。

另外,template和exception的貌似不怎么考,因此比较少T_T

posted on 2010-02-05 01:34  vivy  阅读(332)  评论(0编辑  收藏  举报