l 成员函数&&非成员函数
Eg:
#include<iostream>
using namespace std;
class Student{
public:
void p(){
age=20;
cout<<age;
}
int q(){return(age);}//成员函数q
float score;
private:
char name[9];
int age;
};
void p(int a) //非成员函数p
{
cout<<a;
}
void main()
{
Student a;
void p(int);
a.p();
p(a.q());
}
l 内联函数
何为内联函数?inline、调用之前inline显性定义或者声明(而且声明和定义要一致出现内联关键字
小的成员函数一般在类内定义,一般默认为内联函数)
复杂语句(循环、多分支)不允许(如出现则不视为内联)
C++的工程实现
C++的函数调用机制 介绍函数声明机制
类成员函数不存在于定义类的头文件中,即可以在类定义{}外定义;
为了保证效率尽可能的类内定义,以保证系统自动安排是否内联
l 类的定义和调用的分离
Eg:
//student.h
#include<iostream>
using namespace std;
class student
{
public:
void p();
float score;
protected:
char name;
int age;
};
//p.cpp
#include"student.h"
void student::p()
{
age = 19;
cout<<age;
}
//main.cpp
#include"student.h"
void main()
{
student a;
a.p();
}
将类的定义和类的成员函数分离放在不同文件中避免了函数重复定义的混乱。
l This
this是一个指针,(一个内部调用),所以使用它访问成员时,用->而不是.
Eg:
//student.h
#include<iostream>
using namespace std;
class student
{
public:
student *p(void);
student &q(void);
void s(int);
float score;
protected:
char name;
int age;
};
//p.cpp
#include"student.h"
void student::s(int a)
{
this->age=a;
}
student *student::p()
{
age=age+2;
return this;//this返回指针(地址)
}
student &student::q()
{
age=age+2;
return(*this);//返回引用(指针的值)
}
//main.cpp
#include"student.h"
void main()
{
student a;
a.s(20);
a.score=70;
a.p()->score++;//返回的是指针,使用->
a.q().score++;//返回的是引用,相当于别名使用.
cout<<a.score;
}
l 变量重名
Student.h
class Student{
public:
float q(void);
float score;
protected:
float m;
};
int Student; 允许重名
class Student a;
l Stack(栈)
Eg:
//stacks.h
class stack //定义类
{
public:
void put(int);
int pop(void);
private:
int a;
stack *head,*next;
};//注意,这里的分号不能没有
//stack1.cpp,实现类的成员函数
#include"stacks.h"
void stack::put(int k)
{
stack *p=new stack;
p->a=k;
p->next=head;
head=p;
}
int stack::pop()
{
stack *temp;
int k;
temp=head;
head=head->next;
k=temp->a;
delete temp;
return k;
}
//main.cpp,主函数
#include <iostream>
#include"stacks.h"
using namespace std;
void main()
{
stack *p=new stack;//注意,指针是地址,在使用前必须初始化
p->put(10);
p->put(20);
cout<<p->pop();
cout<<p->pop();
delete p;//在使用结束后释放内存空间
}
l 函数重载
参数类型、参数个数,参数顺序都可以成为重载借口。返回值类型不能够作为重载依据,要求同一个作用域
函数重载的次序:
1. 首先严格匹配
2. 其次相容类型匹配
3. 最后用户定义类型转换(构造函数||运算符重载),尽量避免类型相容二义性。
l 函数默认参数
Eg:
#include<iostream>
using namespace std;
int x(int i=10,int j=20,int k=30)
{
return i+j+k;
}
void main()
{
cout<<x()<<endl;
cout<<x(15)<<endl;
cout<<x(15,25)<<endl;
cout<<x(15,25,35)<<endl;
}
注意:
1. 默认值从右端开始设置,匹配从左端开始匹配。这样的定义是错误的:int x(int i=10,int j,int k=30)
2. 同一作用域内同一函数允许被多次声明
3. 有默认值时不再允许有多次声明。
4. 默认值相同也不允许
理由:可能产生二义性:
void x(int i=10)
{
cout<<i<<"11111";
}
void x()
{
cout<<"22222";
}
void main()
{
x();
}
所以应该避免重载过程中的涉及参数个数的默认设置。
l typedef
typedef int integer;
integer i=2;
typedef float real;
real k=10.10;
typedef char * STRING;
STRING p;
STRING a[10];
typedef struct Student * NODE;
NODE b,c;
l 模板&函数
Eg:
#include<iostream>
using namespace std;
template <typename T>
void change(T &a)
{
a=a+2;
}
template <typename Q>
void swap1(Q &a,Q &b)
{
Q temp;
temp=a;
a=b;
b=temp;
}
template <typename K>
K add(K a,K b)
{
K temp;
temp=a+b;
return temp;
}
void main()
{
int a=2,b=2;
float k=3.1;
change(a);
swap1(a,b);
cout<<a<<b<<endl;
cout<<add<int>(a,k)<<endl;//对于类型不匹配的,可以进行强制类型转换,float类型被转换为int型
//swap1<int>(a,k);//注意,这样是错误的,但这不是因为模板的原因引起的,而是因为引用自身的原因引起的。请看下面的例子
}
void main()
{
int a=1;
float & b = a;//错误
float & c = int(a);//错误
float const & d = a;//正确
}
注意:在引用中,显示转换在类型不一样时是错误的。原因:类型长度不匹配;如int 4字节,double 8字节。
template<typename T,typename U>
void add(T& a,U & b)
{ cout<<a+b<<endl;}
void main(){
int x=1,y=2;
float s=3.0,t=4.0;
add(x,y);
add(s,t);
add(x,t);
add(s,y);
}//以上四条语句均正确;因为T和U可以相同也可以不相同
template<typename T,typename U>
void add(T& a,U & b)
{ cout<<a+b<<endl;}
void swap(T& a,T & b)//错误
{ T temp = a; a = b; b = temp;}
void main(){
int x=1,y=2;
float s=3.0,t=4.0;
add(x,y);
swap(s,t);
}//模板类型声明不能共享;typename不能节省
template <typename T>
void swap(T &a,T &b)
{
T temp=a;
a=b;
b=temp;
}
void swap(int * &a,int * &b)
{
int temp=*a;
*a=*b;
*b=temp;
}
void main()
{
int a=1,b=2;
swap(a,b);
cout<<a<<b<<endl;
float i=5.1,j=5.2;
swap(i,j);
cout<<i<<j<<endl;
int *p=&a,*q=&b;
swap(p,q);
cout<<*p<<*q<<endl;
}//优先匹配非模板函数
l 名字空间namespace
1、namespace的定义可以是不连续的(即namespace的定义是可以积累的),即,同一个namespace可以在不同的文件中定义,分散在不同文件中的同一个namespace中的内容彼此可见。这对生成一个库很有帮助,可以使我们更容易将库的源代码组织成接口和实现部分。如:在头文件(.h文件)的名字空间部分定义库接口;在实现文件(如.c或.cpp文件)的名字空间部分定义库实现。名字空间定义可积累的特性是“向用户隐藏实现细节”必需的,它允许把不同的实现文件(如.c或.cpp文件)编译链接到一个程序中,而不会有编译错误和链接错误。
2、全局名字空间成员,可以用“::member_name”的方式引用。当全局名字空间的成员被嵌套的局部域中声明的名字隐藏时,就可以采用这种方法引用全局名字空间成员。
3、名字空间成员可以被定义在名字空间之外。但是,只有包围该成员声明的名字空间(也就是该成员声明所在的名字空间及其外围名字空间)才可以包含它的定义。
尤其要注意的是#i nclude语句的次序。假定名字空间成员mynamespace::member_i的声明在文件dec.h中,且#i nclude "dec.h"语句置于全局名字空间,那么在include语句之后定义的其他名字空间内,mynamespace::member_i的声明均可见。即,mynamespace::member_i可以在#i nclude "dec.h"之后的任何地方任何名字空间内定义。
4、未命名的名字空间。我们可以用未命名的名字空间声明一个局部于某一文件的实体。未命名的名字空间可以namespace开头,其后不需名字,而用一对花括号包含名字空间声明块。如:
代码:
// 其他代码略namespace{ void mesg() { cout<<"**********\n"; }}int main(){mesg(); //正确 //... return 0;} |
由于未命名名字空间的成员是程序实体,所以mesg()可以在程序整个执行期间被调用。但是,未命名名字空间成员只在特定的文件中可见,在构成程序的其他文件中是不可以见的。未命名名字空间的成员与被声明为static的全局实体具有类似的特性。在C中,被声明为static的全局实体在声明它的文件之外是不可见的。
using关键字
1、using声明与using指示符:前者是声明某名字空间内的一个成员,后者是使用整个名字空间。例如:
代码:
using cpp_primer::matrix; // ok,using声明using namespace cpp_primer; //ok,using指示符 |
2、该using指示符语句可以加在程序文件的几乎任何地方,包括文件开头(#i nclude语句之前)、函数内部。不过用using指定的名字空间作用域(生命周期)受using语句所在位置的生命周期约束。如,函数内部使用“using namespace myspacename;”则 myspacename仅在该函数内部可见。
3、可以用using语句指定多个名字空间,使得多个名字空间同时可见。但这增加了名字污染的可能性,而且只有在使用各名字空间相同成员时由多个using指示符引起的二义性错误才能被检测到,这将给程序的检测、扩展、移植带来很大的隐患。因此,因该尽量使用using声明而不是滥用using指示符