C++的一些基础细节(备考用)
{
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;
这是为什么?
static 变量是这个类共享的,变量是放在全局变量区,不是堆栈里,因此sizeof出来不算在这个static int的大小;是这样的吗??
枚举类型的变量只能进行赋值运算
指针int *p,后不能直接进行*p = 12这样的赋值,因为指针p没有申请内存
如何解释以下程序?
2 {
3 int *a;
4
5 a = (int *)"string";
6 cout<<*a<<endl;
7 cout<<a<<endl;
8 return 0;
9 }
a指向一个string常量的,强制转换为int *,因此a还是"string"常量的首地址,*a应该是取前面几位的值
析构函数的调用不一定要大括号
如
{
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中定义的函数是内联的
2 int y = a;
是可以的
2 int a[size];
是正确的。
但是
2 const int size = x;
3 double classAverage[size];
会报错(为什么?)
2 int a[size[0]];
是错的。
也不能这样定义
2 const S s[] = { { 1, 2 }, { 3, 4 } };
3 double d[s[1].j];
表示p指向的地址是常量,指向的地址不能改变,但是指向的内容可以变
表示p指向的内容是常量,不能改变,但是指向的地址可以改变
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;//错误
是可以的,因为编译器自动转化为
因此不能再改变s的值了。 但是s指向的地址是可以改变的
void g(int x){}
int a = 0;
const int b = 0;
f(a);
g(b);
都是可以的
如果返回值是const,可以赋给non-const,如果不是const 也可以赋给const
如果一个对象定义的时候是const,不能修改这个对象的值
函数中不能修改成员变量
也不能调用非常量函数
如
int node::g()const{f();}
是错的。
不能这样定义
{
private:
const int size;
int a[size];
};
可以把const int 改为static const int
或者用enum来做
引用不能改变
int &x = y;
&x = z;//错误,引用不能改变
不能调用f(t * 3);
不能这样定义
但是可以这样
引用变量的构造函数中也只能初始化,而不能赋值
如果函数的返回值是const的,就不能做左值
在node a = b;或者node a(b);拷贝构造调用。
当函数返回一个对象的时候,拷贝构造被调用
{
node x;
return x;
}
在函数的参数中有对象的时候,调用拷贝构造
{
return 1;
}
但是如果参数的引用,拷贝构造不被调用
{
return 1;
}
静态函数只能对静态变量进行操作
函数中的静态变量只能被初始化一次
static 成员变量在同类的对象中都可见
不能用this来取值
namesapce
可以用using nm::f();
using MyLib::foo;
using MyLib::Cat;
foo();
Cat c;
c.Meow();
}
{
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++(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;
【继承】
输出:
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()
elly.render();
}
Circle circ(60F);
func(circ);
调用的是Circle::render();
但是如果
elly.render();
}
Circle circ(60F);
func(circ);
调用的是Ellipse::render();
析构函数也可以virtual,作用与前面一样
如
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()
void swap(T& a , T& b)
{
T temp;
temp = a;
a = b;
b = temp ;
}
不能这样调用
double b;
swap(a,b);
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的异常处理
try {
while(1)
{
char *p = new char[10000];
}
} catch (bad_alloc& e)
{
}
}
还有一些stream的东西还没看。
另外,template和exception的貌似不怎么考,因此比较少T_T