1、C++基本语法说明
命名空间 namespace
定义:namespace name{
int index;
。。。。。。
} ,大括号内的内容都属于name这个命名空间,用法,name::index,或者先引用using namespace name;然后直接使用index。
c++标准命名空间为std,其所有类库都放在了该命名空间下;
2、构造函数
对象创建时自己调用构造函数,用于初始化对象,构造函数中可以使用参数初始化表,参数初始化表会优先于构造函数先执行.且const修饰的成员变量只能通过初始化列表赋值。初始化列表调用顺序,只和成员变量在类中申明的顺序有关系
另一个关于构造函数的概念是拷贝构造函数,用于当把同一自定义类型的某一对象作为参数来初始化一个新的对象的时候调用的。具体如下三种情况会调用:
1、一个对象以值传递的方式传入函数体
2、一个对象以值传递的方式从函数返回
3、一个对象需要另一个对象进行初始化。
默认的(系统生成的)拷贝构造函数只进行浅拷贝。也就是对象之间的按位拷贝。浅拷贝不会开辟新的堆内存,而是简单的把对象里面成员变量的值复制给另一对象的成员变量。深拷贝则是,若对象中的成员变量分配了堆内存,则拷贝的时候创建新的堆内存,保证数据相同,但指针地址不同。
class Test
{
public:
char *name;
int age;
Test(char*, int);
}
//在初始化列表中,把name1赋值给name,age1赋值给age
Test::Test(char* name1, int age1):name(name1),age(age1)
{
}
//上面函数实现的效果等同于下面的函数
Test::Test(char* name1, int age1)
{
name = name1;
age = age1;
}
Class Test2
{
public:
int len;
char* str;
Test2(int len,char* str);
Test2(const Test2& t);
}
Test2::Test2(int len1, char* str1)
{
len = len1;
str = new char[len];
strcpy(str,str1);
#### }
//拷贝构造函数(深拷贝)
Test2::Test2(const Test2& t)
{
len = t.len;
str = new char[len];
if(str!=NULL)
strcpy(str,t.str);
}
//拷贝构造函数(浅拷贝)
Test2::Test2(const Test2& t)
{
len = t.len;
str = t.str;
}
3 析构函数
析构函数是用于在对象被回收前,清理对象。一般情况,对象在其作用域结束或调用delete释放时会调用。static和全局对象的作用域是全局的,故此类对象在程序结束时才会被释放。
4 inline函数
C++ 用内联函数取代C的带参宏定义,使用inline关键字在类体中定义的成员函数为内联(inline)函数,在类体外定义的不是。
class Test{
public:
inline void say(); //声明为内联函数
};
//内联函数定义
void Student::say(){
printf("");
}
5 this指针
this是一个常量指针,指向当前对象的首地址。通过this可以访问当前对象的成员变量和成员函数。 this 是一个常量指针,故无法修改这个指针,且其调用方式是this->xxx。
this指针是作为成员函数的参数隐式传递的。其本质是成员函数的一个局部变量,在调用成员函数的适合赋值,调用结束后销毁。this 只能在成员函数内部使用。不能在类函数(静态成员函数)中调用
6 const
参考这篇博客
7 友元函数和友元类
友元的关系是单向的而不是双向的。
友元的关系不能传递.
友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。友元函数可以访问当前类中的所有成员,包括 private 属性的。
class TestFriend
{
public:
string name;
TestFriend(string name);
friend void display(TestFriend&);
};
TestFriend::TestFriend(string name)
{
this->name = name;
}
void display(TestFriend &t)
{
cout<<t.name<<endl;
}
int main()
{
TestFriend t("felix");
display(t);
}
友元类,在A类中申明:
friend 类B
则,友元类B中的所有函数都是A类的友元函数,可以访问A类中的所有成员.
8 派生类
8.1 派生类构造函数
基类的构造函数不能被继承,在声明派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数来完成。所以在设计派生类的构造函数时,不仅要考虑派生类新增的成员变量,还要考虑基类的成员变量,要让它们都被初始化。故一般是在派生类的构造函数时,调用基类的构造函数
派生类有多个基类的时候,其基类的构造函数以逗号分隔,且执行顺序只与继承的时候的申明的顺序有关系。先调用父类构造函数,再调用子类构造函数。
8.2 explicit(此特性在xcode貌似没有)
在C++中,凡是一个类有只带一个参数的构造函数,默认都定义了一组隐式转换,把构造函数的参数类型转换为该class的类型。比如Integer(int)就定义了一个从int到Integer类型的隐式转换。要去掉此特性,则必须在构造函数前加explicit。表面此构造方法只能显式调用。
8.3 派生类的析构函数
和构造函数类似,析构函数也是不能被继承的。且一个类只能有一个析构函数。
对于析构函数,调用顺序与构造函数调用顺序相反,即先执行派生类的析构函数,然后再执行基类的析构函数。
8.4 派生类方法调用(命名冲突)
当两个基类中有同名的成员方法和成员函数时,就会产生命名冲突,这时不能直接访问该成员,需要加上类名和域解析符。例如sub.Super1::commonMethod();
使用派生类实例化基类的时候(即基类的指针指向派生类时),若基类和派生类有同名方法的时候,默认只会调用到基类的方法,而不会调用派生类方法。
基类对象不能赋值给派生类对象,只能派生类对象赋值给基类对象,且赋值仅仅是对应的成员变量的赋值,成员函数和this指针还是之前的不变。若是基类中成员函数申明有virtual修饰,才可能调用到派生类的成员函数。
Super2 *s2 = new Sub("王若初",0,9999999);
s2->commonMethod();//这里默认调用父类的方法,而不是子类的方法。
若是想实现调用派生类的方法,则需要在基类的该方法申明的地方加上virtual关键字。(此时派生类默认也会被加上),然后就会默认调用到派生类的方法,而不是基类的方法。这里其实是使用了覆盖的特性:覆盖的函数前必须加关键字Virtual
此处还需要注意的是:对于使用派生类实例化基类(即基类的指针指向派生类)这种情况,父类的析构函数也必须加上virtual,否则无法调用到子类的析构函数。
9 虚基类
适用于菱形继承的情况,例如A->B->D A->C->D,这里,默认情况下,在D中就会产生两个A。为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类,否则仍然会出现对基类的多次继承。 申明继承的时候,如下:class Super2:virtual public Super
纯虚函数
virtual 函数返回类型 函数名 (函数参数) = 0;
纯虚函数没有函数体,只有函数声明,在虚函数声明结尾加上=0,表明此函数为纯虚函数.只有类中的虚函数才能被声明为纯虚成员函数,普通成员函数和顶层函数均不能声明为纯虚函数.
10 RTTI机制
1、dynamic_cat注意事项:
只能应用于指针和引用的转换
要转换的类型中必须包括虚函数
转换成功,返回子类地址,转换失败,返回null
2、type_id
type_id返回一个type_info对象的引用
如果想要通过基类指针获得派生类的数据类型,基类必须带有虚函数
只能获取对象的实际类型
11 运算符重载
一元运算符重载
例如 ++ 运算符的重载分为前置和后置两种情况。
A& operator ++ ();//前置++
A operator ++ (int);//后置++
二元运算符重载
例 + 运算符的重载,方式一为友元函数方式,方式二为成员函数方式,两张方式最终效果相同。
friend complex operator+(const complex &a, const complex &b)
complex operator+(const complex &a, const complex &b)
{
complex c;
c.real = a.real + b.real;
c.imag = a.imag + b.imag;
return c;
}
complex operator+(const complex &a);
complex complex::operator+(const complex &a)
{
complex c;
c.real = a.real + real;
c.imag = a.imag + imag;
return c;
}
运算符重载部分详情可以参考另一篇博客
12 模板
12.1 函数模板
函数模板,实际上是建立一个通用函数,其返回值类型和形参类型不具体指定,用一个虚拟的类型来代替,这个通用函数就称为函数模板(Function Template)
定义模板函数的语法为:
template <typename 数据类型参数 , typename 数据类型参数 , ...> 返回值类型 函数名(形参列表){
//TODO:
//在函数体中可以使用数据类型参数
}
其中template 是定义模板函数的关键字,template后面的尖括号不能省略;typename 是声明数据类型参数名的关键字,多个数据类型参数以逗号分隔。
template <typename T>
T sum(const T a, const T b) {
return a+b;
}
int main()
{
cout<<sum(10, 20)<<endl;
cout<<sum(10.12, 20.23)<<endl;
}
12.2 类模板
声明模板类的语法为:
template<typename 数据类型参数 , typename 数据类型参数 , …> class 类名{
//TODO:
};
以 template 开头,后跟数据类型参数列表;数据类型参数不能为空,多个参数用逗号隔开。声明了模板类,就可以用数据类型参数来声明类中的成员变量和成员函数。
13 string
与C风格的 char* 字符串不同,string 类型的变量结尾没有 ‘\0’
string 类为我们提供了一个转换函数 c_str(),该函数能够将 string 变量转换为一个 char* 字符串数组的形式,并将指向该数组的指针返回
string filename = "input.txt";
ifstream in;
in.open(filename.c_str());
13.1string类字符串的拼接 直接使用+ 或+=
string s1, s2, s3;
s1 = "first";
s2 = "second";
s3 = s1 + s2;
cout<< s3 <<endl;
s2 += s1;
cout<< s2 <<endl;
s1 += "third";
cout<< s1 <<endl;
s1 += 'a';
cout<< s1 <<endl;
13.2 string类字符串插入
string& insert (size_t pos, const string& str);
pos 表示要插入的位置,也就是下标;str 表示要插入的字符串,它可以是 string 变量,也可以是C风格的字符串。
string s1, s2, s3;
s1 = s2 = "1234567890";
s3 = "aaa";
s1.insert(5, s3);
cout<< s1 <<endl;
s2.insert(5, "bbb");
cout<< s2 <<endl;
13.3 删除字符串
string& erase (size_t pos = 0, size_t len = npos);
pos 表示要删除的子字符串的起始下标,len 表示要删除子字符串的长度。如果不指明 len 的话,那么直接删除从 pos 到字符串结束处的所有字符(此时 len = str.length - pos)。
string s1, s2, s3;
s1 = s2 = s3 = "1234567890";
s2.erase(5);
s3.erase(5, 3);
cout<< s1 <<endl;
cout<< s2 <<endl;
cout<< s3 <<endl;
13.4 提取子字符串
string substr (size_t pos = 0, size_t len = npos) const;
pos 为要提取的子字符串的起始下标,len 为要提取的子字符串的长度。
string s1 = "first second third";
string s2;
s2 = s1.substr(6, 6);
cout<< s1 <<endl;
cout<< s2 <<endl;
13.5 字符串查找
size_t find (const string& str, size_t pos = 0) const; //查找对象--string类对象
size_t find (const char* s, size_t pos = 0) const; //查找对象--字符串
size_t find (const char* s, size_t pos, size_t n) const; //查找对象--字符串的前n个字符
size_t find (char c, size_t pos = 0) const; //查找对象--字符
第一个参数为待查找的子字符串,它可以是 string 变量,也可以是C风格的字符串。第二个参数为开始查找的位置(下标);如果不指明,则从第0个字符开始查找。
string s1 = "first second third";
string s2 = "second";
int index = s1.find(s2,5);
if(index < s1.length())
cout<<"Found at index : "<< index <<endl;
else
cout<<"Not found"<<endl;
rfind()与find()类似,只是从反向查找
find_first_of() 用于查找子字符串和字符串共同具有的字符在字符串中首次出现的位置
14 异常
在C++中,我们可以捕获上面的异常,避免程序崩溃。捕获异常的语法为:
try{
// 可能抛出异常的语句
}catch(异常类型){
// 处理异常的语句
}
14.1 异常类型
所谓抛出异常,实际上是创建一份数据,这份数据包含了错误信息,程序员可以根据这些信息来判断到底出了什么问题,接下来该怎么处理
C++规定,异常类型可以是基本类型,也可以是标准库中类的类型,还可以是自定义类的类型。
C++语言本身以及标准库中的函数抛出的异常,都是 exception 类或其子类的类型。也就是说,抛出异常时,会创建一个 exception 类或其子类的对象。
14.2 throw
throw exceptionData;
throw 既可以用在标准库中,也可以用在自定义的函数中,抛出我们期望的异常.exceptionData 是“异常数据”的意思,它既可以是一个普通变量,也可以是一个对象,只要能在 catch 中匹配就可以。
如果希望能够抛出多种类型的异常,可以用逗号隔开:
double func (char param) throw (int, char, exception);//可以跑出int, char, exception异常
如果不希望限制异常类型,那么可以省略:
double func (char param) throw (); //func() 函数可以抛出任何类型的异常
14.3 exception类
C++语言本身或者标准库抛出的异常都是 exception 的子类,称为标准异常(Standard Exception)
try{
//可能抛出异常的语句
}catch(exception &e){ //这里可以匹配所有的标准异常
//处理异常的语句
}
exception文件源码
class exception {
public:
exception () throw(); //构造函数
exception (const exception&) throw(); //拷贝构造函数
exception& operator= (const exception&) throw(); //运算符重载
virtual ~exception() throw(); //虚析构函数
virtual const char* what() const throw(); //虚函数
}
异常类继承图可以查看