c++基础
多文件结构和编译预处理命令
C++程序的一般组织结构
•一个源程序可以划分为多个源文件:
-
类声明文件(.h文件)
-
类实现文件(.cpp文件)
-
类的使用文件(main()所在的.cpp文件)
外部变量
•在所有类之外声明的函数(也就是非成员函数),都是具有文件作用域的。
•这样的函数都可以在不同的编译单元中被调用,只要在调用之前进行引用性声明(即声明函数原型)即可。也可以在声明函数原型或定义函数时用extern修饰,其效果与不加修饰的缺省状态是一样的。
namespace
•使用匿名的命名空间:在匿名命名空间中定义的变量和函数,都不会暴露给其它的编译单元。
**namespace** { //匿名的命名空间
int n;
void f() {
n++;
}
}
•这里被“namespace { …… }”括起的区域都属于匿名的命名空间。
访问权限
-
public:所有
-
private:本类,友元
-
protected:本类,派生类,友元
重载
- 重载函数参数要不同(类型,个数或者顺序不同)
内联函数
- 为了提高运行效率
- 不要有复杂的结构(循环,swich语句)
- 将函数体放在类的声明中
- 使用inline关键字
- 类结构中所在的类说明内部定义的函数是内联函数。
带默认参数的函数
- 类中声明的时候带默认参数,具体实现的时候不带
构造函数
- 函数名和类名完全相同
- 不写构造函数时,系统会默认给一个无参的默认构造函数
- 个数大于等于一个
析构函数
- 对象销毁时被调用
- 不能重载,有且只有一个
委托构造函数
- C++11 引入
复制(拷贝)构造函数
#include <iostream>
using namespace std;
class Point { //Point 类的定义
public: //外部接口
Point(int xx = 0, int yy = 0) { //构造函数
x = xx;
y = yy;
}
Point(const Point &p); //拷贝构造函数
int getX() {
return x;
}
int getY() {
return y;
}
private: //私有数据
int x, y;
};
//成员函数的实现
Point::Point(const Point &p) {
x = p.x;
y = p.y;
cout << "Calling the copy constructor" << endl;
}
//形参为Point类对象的函数
void fun1(Point p) {
cout << p.getX() << endl;
}
//返回值为Point类对象的函数
Point fun2() {
Point a(1, 2);//临时对象
return a;
}
//主程序
int main() {
Point a(4, 5); //第一个对象A
Point b = a; //情况一,用A初始化B。第一次调用拷贝构造函数
cout << b.getX() << endl;
fun1(b); //情况二,对象B作为fun1的实参。第二次调用拷贝构造函数
b = fun2(); //情况三,函数的返回值是类对象,函数返回时,调用拷贝构造函数,赋值(此时b不构造)
cout << b.getX() << endl;
cout << b.getY() << endl;
return 0;
}
- 函数返回对象时会增加一个临时对象,增加开销(情况三)
移动构造函数
c++11标准
左值:赋值语句左侧的对象变量。
右值:赋值语句右侧的值,不依附于对象。
float n=6;
float &lr_n=n; //对变量n的左值引用
float &&rr_n=n; //错误,不能将右值引用绑定到左值n上
float &&rr_n=n*n; //将乘法结果左值绑定到左值引用
float &lr_n=n*n; //错误,不能将左值引用绑定到右值n*n上
标准库utility中声明提供了move函数,将左值对象移动成为右值。
float n=10;
float &&rr_n = std::move(n);
default、delete函数(C++11标准)
组合
•类中的成员数据是另一个类的对象。
•可以在已有抽象的基础上实现更复杂的抽象。
原则:不仅要负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化。
声明形式:
类名::类名(对象成员所需的形参,本类成员形参)
:对象1(参数),对象2(参数),......
{
//函数体其他语句
}
构造组合类对象时的初始化次序
•首先对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员)进行初始化,初始化次序是成员在类体中定义的次序。
▫成员对象构造函数调用顺序:按对象成员的声明顺序,先声明者先构造。
▫初始化列表中未出现的成员对象,调用用默认构造函数(即无形参的)初始化
•处理完初始化列表之后,再执行构造函数的函数体。
eg类的组合,线段(Line)类
#include <iostream>
#include <cmath>
using namespace std;
//Point类省略
//类的组合
class Line { //Line类的定义
public: //外部接口
Line(Point xp1, Point xp2);
Line(Line &l);
double getLen() { return len; }
private: //私有数据成员
Point p1, p2; //Point类的对象p1,p2
double len;
};
//组合类的构造函数
Line::Line(Point xp1, Point xp2) : p1(xp1), p2(xp2) {
cout << "Calling constructor of Line" << endl;
double x = static_cast<double>(p1.getX() - p2.getX());
double y = static_cast<double>(p1.getY() - p2.getY());
len = sqrt(x * x + y * y);
}
Line::Line (Line &l): p1(l.p1), p2(l.p2) {//组合类的复制构造函数
cout << "Calling the copy constructor of Line" << endl;
len = l.len;
}
int main() {
Point myp1(1, 1), myp2(4, 5); //建立Point类的对象
Line line(myp1, myp2); //建立Line类的对象
Line line2(line); //利用复制构造函数建立一个新对象
cout << "The length of the line is: ";
cout << line.getLen() << endl;
cout << "The length of the line2 is: ";
cout << line2.getLen() << endl;
return 0;
}
前向引用声明
class B; //前向引用声明
class A {
public:
void f(B b);
};
class B {
public:
void g(A a);
};
作用域
//5_1.cpp
#include <iostream>
using namespace std;
int i; //全局变量,文件作用域
int main() {
int i = 5; //定义局部变量i并赋值
{ //子块1
int i; //局部变量,局部作用域
i = 7;
cout << "i = " << i << endl;//输出7
}
cout << "i = " << i << endl;//输出5
::i = 10;
cout << "i = " << i << endl;//输出5
cout <<"i = " << ::i << endl;//输出10
return 0;
}
静态数据成员
- ▫用关键字static声明
- 必须在类外定义和初始化,用*(::)来指明所属的类
eg :具有静态数据成员的Point类
#include <iostream>
using namespace std;
class Point { //Point类定义
public: //外部接口
Point(int x = 0, int y = 0) : x(x), y(y) { //构造函数
//在构造函数中对count累加,所有对象共同维护同一个count
count++;
}
Point(Point &p) { //复制构造函数
x = p.x;
y = p.y;
count++;
}
~Point() { count--; }
int getX() { return x; }
int getY() { return y; }
void showCount() { //输出静态数据成员
cout << " Object count = " << count << endl;
}
private: //私有数据成员
int x, y;
static int count; //静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定
int main() { //主函数
Point a(4, 5); //定义对象a,其构造函数回使count增1
cout << "Point A: " << a.getX() << ", " << a.getY();
a.showCount(); //输出对象个数
Point b(a); //定义对象b,其构造函数回使count增1
cout << "Point B: " << b.getX() << ", " << b.getY();
b.showCount(); //输出对象个数
return 0;
}
静态成员函数
没有this指针
静态成员函数无法访问成员变量
#include <iostream>
using namespace std;
class Point { //Point类定义
public: //外部接口
Point(int x = 0, int y = 0) : x(x), y(y) { //构造函数
//在构造函数中对count累加,所有对象共同维护同一个count
count++;
}
Point(Point &p) { //复制构造函数
x = p.x;
y = p.y;
count++;
}
~Point() { count--; }
int getX() { return x; }
int getY() { return y; }
static void showCount() { //输出静态数据成员
cout << " Object count = " << count << endl;
}
private: //私有数据成员
int x, y;
static int count; //静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定
int main() { //主函数
Point::showCount();
Point a(4, 5); //定义对象a,其构造函数回使count增1
cout << "Point A: " << a.getX() << ", " << a.getY();
a.showCount(); //输出对象个数
Point b(a); //定义对象b,其构造函数回使count增1
cout << "Point B: " << b.getX() << ", " << b.getY();
b.showCount(); //输出对象个数
Point::showCount();
return 0;
}
友元
友元不是成员函数
友元使用的原因
结合着类的特性,可知:类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。
为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数,但它需要在类体内进行声明,为了与该类的成员函数加以区别,在声明时前面加以关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。
优缺点
利用 friend 修饰符,可以让一些普通函数 或 另一个类的成员函数 直接对某个类的保护成员和私有成员进行操作,提高了程序的运行效率;同时避免把类的成员都声明为public,最大限度地保护数据成员的安全。
但是,即使是最大限度地保护数据成员,友元也破坏了类的封装性。
如果将类的封装比喻成一堵墙的话,那么友元机制就像墙上开了一个门。所以使用友元时一定要慎重。
eg友元函数 (计算两点间的距离)
#include <iostream>
#include <cmath>
using namespace std;
class Point { //Point类声明
public: //外部接口
Point(int x=0, int y=0) : x(x), y(y) { }
int getX() { return x; }
int getY() { return y; }
friend float dist(Point &a, Point &b);
private: //私有数据成员
int x, y;
};
float dist( Point& a, Point& b) {
double x = a.x - b.x;
double y = a.y - b.y;
return static_cast<float>(sqrt(x * x + y * y));
}
int main() {
Point p1(1, 1), p2(4, 5);
cout <<"The distance is: ";
cout << dist(p1, p2) << endl;
return 0;
}
友元类
声明
friend class A;
friend void A::print();
eg
class A {
friend class B;
public:
void display() {
cout << x << endl;
}
private:
int x;
}
class B {
public:
void set(int i);
void display();
private:
A a;
};
void B::set(int i) {
a.x=i;//由于B是友元类,故B中函数可访问A中的x
}
void B::display() {
a.display();
}
友元关系是单向的
上面案例如果声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护数据,但A类的成员函数却不能访问B类的私有、保护数据。
共享数据的保护
•对于既需要共享、又需要防止改变的数据应该声明为常类型(用const进行修饰)。对于不改变对象状态的成员函数应该声明为常函数。
-
常对象:必须进行初始化,不能被更新。
const 类名 对象名
class A
{
public:
A(int i,int j) {x=i; y=j;}
...
private:
int x,y;
};
A const a(3,4); //a是常对象,不能被更新
通过常对象只能调用它的常成员函数。
-
常成员
用const进行修饰的类成员:常数据成员和常函数成员
- 常函数成员
使用const关键字说明的函数。
常成员函数不更新对象的数据成员。
常成员函数说明格式:
类型说明符 函数名(参数表)const;
这里,const是函数类型的一个组成部分,因此在实现部分也要带const关键字。const关键字可以被用于参与对重载函数的区分
常对象只能调用常成员函数,并且优先调用普通成员函数
- eg const关键字可以被用于参与对重载函数的区分
#include<iostream>
using namespace std;
class R {
public:
R(int r1, int r2) : r1(r1), r2(r2) { }
void print();
void print() const;
private:
int r1, r2;
};
void R::print() {
cout << r1 << ":" << r2 << endl;
}
void R::print() const {
cout << r1 << ";" << r2 << endl;
}
int main() {
R a(5,4);
a.print(); //调用void print()
const R b(20,52);
b.print(); //调用void print() const
return 0;
}
-
eg 常对象只能调用常成员函数
#include<iostream> using namespace std; class R { public: R(int r1, int r2) : r1(r1), r2(r2) { } void print(); private: int r1, r2; }; void R::print() { cout << r1 << ":" << r2 << endl; } int main() { R a(5,4); a.print(); //调用void print() const R b(20,52); b.print();//出错 return 0; }
-
常数据成员
#include <iostream> using namespace std; class A { public: A(int i); void print(); private: const int a; static const int b; //静态常数据成员 }; const int A::b=10; A::A(int i) : a(i) { }//只能这样初始化常数据成员,不能出现为a赋值的操作 void A::print() { cout << a << ":" << b <<endl; } int main() { /*建立对象a和b,并以100和0作为初值,分别调用构造函数,通过构造函数的初始化列表给对象的常数据成员赋初值*/ A a1(100), a2(0); a1.print(); a2.print(); return 0; }
-
常引用:被引用的对象不能被更新。
const 类型说明符 &引用名
#include <iostream>
#include <cmath>
using namespace std;
class Point { //Point类定义
public: //外部接口
Point(int x = 0, int y = 0)
: x(x), y(y) { }
int getX() { return x; }
int getY() { return y; }
friend float dist(const Point &p1, const Point &p2);
private: //私有数据成员
int x, y;
};
float dist(const Point &p1, const Point &p2) {
double x = p1.x - p2.x;
double y = p1.y - p2.y;
return static_cast<float>(sqrt(x * x + y * y));
}
int main() { //主函数
const Point myp1(1, 1), myp2(4, 5);
cout << "The distance is: ";
cout << dist(myp1, myp2) << endl;
return 0;
}
-
常数组:数组元素不能被更新
类型说明符 const 数组名[大小]
-
常指针:指向常量的指针
对象指针
•声明形式
类名 *对象指针名;
例: Point a(5,10);
Piont *ptr;
ptr=&a;
•通过指针访问对象成员
对象指针名->成员名
ptr->getx() 相当于 (*ptr).getx();
动态创建对象(new delete)
#include <iostream>
using namespace std;
class Point {
public:
Point() : x(0), y(0) {
cout<<"Default Constructor called."<<endl;
}
Point(int x, int y) : x(x), y(y) {
cout<< "Constructor called."<<endl;
}
~Point() { cout<<"Destructor called."<<endl; }
int getX() const { return x; }
int getY() const { return y; }
void move(int newX, int newY) {
x = newX;
y = newY;
}
private:
int x, y;
};
int main() {
cout << "Step one: " << endl;
Point *ptr1 = new Point;//调用缺省构造函数
delete ptr1; //删除对象,自动调用析构函数
cout << "Step two: " << endl;
ptr1 = new Point(1,2);
delete ptr1;
Point *ptr = new Point[2]; //创建对象数组
ptr[0].move(5, 10); //通过指针访问数组元素的成员
ptr[1].move(15, 20); //通过指针访问数组元素的成员
cout << "Deleting..." << endl;
delete[] ptr; //删除整个对象数组
return 0;
}
string类
•常用构造函数
▫string(); //缺省构造函数,建立一个长度为0的串
▫string(const char *s); //用指针s所指向的字符串常量初始化string类的对象
▫string(const string& rhs); //拷贝构造函数
•例:
▫string s1; //建立一个空字符串
▫string s2 = “abc”; //用常量建立一个初值为”abc”的字符串
▫string s3 = s2;//执行拷贝构造函数,用s2的值作为s3的初值
•常用操作符
▫s + t 将串s和t连接成一个新串
▫s = t 用t更新s
▫s == t 判断s与t是否相等
▫s != t 判断s与t是否不等
▫s < t 判断s是否小于t(按字典顺序比较)
▫s <= t 判断s是否小于或等于t (按字典顺序比较)
▫s > t 判断s是否大于t (按字典顺序比较)
▫s >= t 判断s是否大于或等于t (按字典顺序比较)
▫s[i] 访问串中下标为i的字符
•例:
▫string s1 = "abc", s2 = "def";
▫string s3 = s1 + s2; //结果是"abcdef"
▫bool s4 = (s1 < s2); //结果是true
▫char s5 = s2[1]; //结果是'e'
用getline输入整行字符串
•输入整行字符串
▫用cin的>>操作符输入字符串,会以空格作为分隔符,空格后的内容会在下一回输入时被读取
▫用string头文件中的getline可以输入整行字符串,例如:
getline(cin, s2);
•以其它字符作为分隔符输入字符串
▫输入字符串时,可以使用其它分隔符作为字符串结束的标志(例如逗号、分号)
▫把分隔符作为getline的第3个参数即可,例如:
getline(cin, s2, ',');
include <iostream>
#include <string>
using namespace std;
int main() {
for (int i = 0; i < 2; i++) {
string city, state;
getline(cin, city, ',');
getline(cin, state);
cout << "City:" << city << “ State:" << state << endl;
}
return 0;
}
热门相关:战斗就变强 惊悚乐园 重生全能悍妻:张狂大小姐 我的末世基地车 九星毒奶