C++Primer学习——类
我们在创建类的对象时,类不应该仅仅被声明,还应该被定义过,否则无法知道类占用了多少的内存
但是如果一个类的名字已经出现过就被认为是已经声明过了,所以允许包含自己的指针或者引用。
默认构造函数:
当类中包含一个其他类的成员且它没有默认构造函数,那个编译器无法为当前类合成默认构造函数。
如果不支持内类初始值,那么所有构造函数都应显式的初始化每个内置类型成员
使用vector或string能避免分配、释放内存带来的复杂性
struct和class唯一的区别就是默认访问权限,struct(public) class(private)
初始化列表:
对象成员的初始化发生在进入构造函数本体之前,构造函数内的只能算是赋值
如果没有初始值列表显示初始化成员,会在构造函数之前执行默认初始化
(如果想创建一个常量对象,构造函数完成初始化过程,对象才真正有常量属性)。
class fo
{
public:
fo(int i);
private:
int ai;
const int ci;
int &ri;
}
fo::fo(int i)
{
ai = i;
ci = i; //error 不能给const赋值
ri = i; //error ri未初始化
}
fo::fo(int i):ai(i),ci(i),ri(i){} //correct
//如果成员是const,引用或者某需要提供默认初始值的类类型,要通过构造函数初始值列表提供。
如果构造函数没有初始值列表,那么我构造函数之前会先进行默认初始化
委托构造函数:
class sales
{
public:
sales(string s,int cnt):bookNo(s),units_sold(cnt){} //1
sales():sales("",0,0) {} //2 利用1
sales(string s):sales(s,0,0){} //3 利用1
sales(iostream &is):sales(){} //4 利用2
}
数据成员:
类内部的成员函数和友元函数是隐式内联的。
const成员函数(修改隐式this指针的类型):
所以一个成员函数被标记为const,则它不能调用一个非const的成员函数(隐式this指针,无法从const转变成非const)
class aa{
int num;
public:
void out2() const{
cout<<num<<endl;
}
void out3() const{
num+=10; //error,const函数不能修改其数据成员
cout<<num<<endl;
}
};
定义在类内部的函数是隐式inline.结构体内部this指向自己,一个常量指针.
但在const成员函数中,this会被修改成 指向常量的常量指针,所以无法修改值
编译器会首先编译成员声明,然后才是成员函数
友元
有的函数是类接口的一部分,但又不是类的成员。友元可以允许其它类或者函数访问它的非公有成员。
友元只能声明在类内部,不是类的成员所以不受它所在区域的访问控制级别的约束。
不具有传递性、不能被继承、单向无交换性
令成员函数作为友元,注意作用域:
先定义man,声明disp不定义。在disp使用woman前声明它。
定义woman然后disp的友元声明。
定义disp
//成员函数的friend声明必需在定义之前,需要用到类限定符。所以man必须先被定义
class woman; // 前向声明
class man
{
public:
void disp(woman &w);
};
class woman
{
public:
friend void man::disp(woman &w); // 将man的其中一个成员函数disp()设为woman的友元函数,就可以使用该函数访问woman对象的私有成员了
private:
string name;
};
void man::disp(woman &w)
{
cout << w.name << endl;
}
类和非成员函数的声明不是必须在它们的友元声明之前。
struct X
{
friend void f() {]
X(){ f(); }
void g();
void h();
}
void X::g(){ return f(); }
void f();
void X::h(){ return f(); }
如果想把一组重载声明为友元,必需对戏中的每一个分别声明
可变数据成员:
有时候希望改变const对象里面的某个数据成员,可通过在数据成员加上mutable实现。
而一个可变的数据成永远不可能是const
mutable size_t acces;
返回*this的成员函数:
返回引用和非引用的区别
class Screen
{
public:
Screen &set(char);
Screen &move(int ,int);
};
inline Screen& Screen::set(char c){}
inline Screen& Screen::move(int x,int y){}
int main()
{
Screen myScreen;
myScreen.move(4,0).set('#');
myScreen.move(4,0); //如果返回引用,等效于此
myScreen.set('#');
Screen temp = myScreen.move(4,0); //如果返回非引用。。,则是一个副本
temp.set('#');
}
const成员返回*this时,不能嵌入一个动作序列
如果display返回常量引用,调用set则会产生错误。。无法修改一个常量对象
基于const的重载:
如果某个对象上面调用display,该对象是否是const决定了调用display的那个版本
Class A {
int display();
int display() const;
};
原因是:按照函数重载的定义,函数名相同而形参表有本质不同的函数称为重载。在类中,由于隐含的this形参的存在,const版本的 display函数使得作为形参的this指针的类型变为指向const对象的指针
不完全类型:
一旦一个类名出现,就被认为是声明过了(但尚未定义),因此允许包含自己的指针。
在声明之后,定义之前是一个不完全类型。
①可以定义指向这个的指针和引用 ②也可以声明以不完全类型作为参数或者返回类型的函数
class Link_screen
{
Screen windows;
Link_screen *next;
}
作用域:
一旦遇到类名,那么剩下的部分就在类的作用域之内了(参数列表和函数体)
class Window_mgr
{
public:
ScreenIndex addScreen(const Screen&);
};
Window_mgr::ScreenIndex
Window_mgr::addScreen(const Screen& a)
{}
对于类成员函数的名字查找:
1.编译成员的声明 2.直到类全部可见之后才编译函数体
class Account //balance会在整个类可见之后才被处理
{
public:
int balance(){return val;}
private:
int val;
}
类型名:
通常内层作用域可以重新定义作用域外的名字,即便这个已经被用过。
但是在类中,如果使用外层作用域的某个名字,而这名字代表一种类型,则不能重新定义
typedef double mon;
class Account{
public:
mon balance(){};
private:
typedef double mon; //不能重新定义mon
}
成员定义的普通块作用域的名字查找:
1.先成员函数内部 2.类内全面查找 3.成员函数定义之前的作用域
所以此例中生效的是pos height,但是它隐藏了同名的成员,对于被隐藏对象可以通过作用域符 ::
int height;
class Screen{
public:
typedef std::string::size_type pos;
void dup(pos height)
{
cursor = width*height; //width*this->height,不建议隐藏同名
}
private:
pos height = 0,width = 0;
pos curson;
}
类类型转换:
隐式转换:
能通过一个参数的拷贝构造函数定义一条从构造函数参数类型到类类型的隐式转换规则。
(只有一个实参的能用于隐式转换)
struct foo
{
foo(){}
foo(int a):real(a){}
// int b = 2;
const foo& operator+(foo c){
return foo(this->real + c.real);
}
int real;
};
int main()
{
foo f;
f = 10; //会生成一个临时的foo
cout <<f.real <<endl;
f = f + 100;
cout << f.real <<endl;
}
只允许一步转换:
string->foo correct
"9999"->string->foo error
item.combine("9999"); //error
item.combine(string("9999")); //correct 显式转换string,隐式转换
item.combine(sales("9999")); //correct 隐式转换string,显式转换sales
我们可以通过将构造函数声明为explicit阻止隐式转换,函数只能用于直接初始化
(只类内部的声明时使用,在类外定义时不需要重复explicit)
struct node
{
explicit node(){}
explicit node(string s){}
node& combine(node &b){}
};
int main()
{
string s = "xxx";
node a("xxx");
node b(s);
//node a = s; //error 没法隐式的创建对象node
//a.combine(s); //error
}
静态成员:
static将其与类绑定起来。在类外定义时不能重复static.一般来说不能在类内定义初始化静态成员(可能产生重复定义)
如果静态成员是constexpr类型并且初始值是常量表达式,可以在类内进行定义。
而且由于静态成员函数不与对象绑定,不包含this指针,所以也不能被声明为const
因为静态成员函数属于整个类,在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间,所以这个调用就出错了
class Point
{
public:
void init()
{
}
static void output()
{
printf("%d\n", m_x); //error
}
private:
int m_x;
};
int main()
{
Point pt;
pt.output();
}
static数据成员,不可被定义在inline函数中.inline函数在编译时会被编译为2进制代码并重复嵌入各函数体中,而static类型数据成员只可被初始化一次,inline函数中使用static的话,将会造成static类型数据成员被多次初始化错误.
静态成员可以是不完全类型,静态成员可以作为默认实参
class Screen
{
public:
Screen &clear(char = back);
private:
static const char back;
};
C++Primer学习——类的更多相关文章
- C++ Primer学习笔记(三) C++中函数是一种类型!!!
C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...
- C++ Primer学习笔记(二)
题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接 C++ Primer学习笔记(一) 27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...
- 深入java虚拟机学习 -- 类的加载机制(续)
昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...
- 深入java虚拟机学习 -- 类的卸载
类的生命周期 在开始本节之前让我们再来回顾下类的生命周期 没看过前6个过程的同学建议从头看下<深入java虚拟机学习 -- 类的加载机制>,这里就不再过多介绍了,着重说下类的卸载 类的卸载 ...
- Swift学习——类的定义,使用,继承,构造等(五)
Swift学习--类的定义,使用.继承,构造等(五) 类的使用说明 1 使用class和类名来创建一个类名,比如: class student 2 类中属性的声明和常量和变量一样,唯一的差别就是他们的 ...
- c++ primer 学习杂记2【派生类到基类转换的可访问性】
参考: http://blog.csdn.net/rehongchen/article/details/7930853 http://blog.csdn.net/ming_road/article/d ...
- C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域
面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:假设不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这样的类作用域的层次嵌套使 ...
- C++ Primer 学习笔记_57_类和数据抽象 --管理指针成员
复印控制 --管理指针成员 引言: 包括指针的类须要特别注意复制控制.原因是复制指针时.一个带指针成员的指针类 class HasPtr { public: HasPtr(int *p,int i): ...
- C++ Primer 学习笔记_53_类和数据抽象 --友元、static员
分类 --友元.static成员 一.友元 友元机制同意一个类将对其.友元关系:一个样例 如果一个窗体管理类Window_Mgr可能须要訪问由其管理的Screen对象的内部数据.Screen应该同意其 ...
随机推荐
- Archlinux下i3wm与urxvt的配置
前段时间学习了GitHub的两位前辈:Airblader和wlh320.他们的相关教程在https://github.com/Airblader/i3和https://github.com/wlh32 ...
- 结合Socket实现DDoS攻击
一.实验说明 1. 实验介绍 通过上一节实验的SYN泛洪攻击结合Socket实现DDoS攻击. 2. 开发环境 Ubuntu Linux Python 3.x版本 3. 知识点 本次实验将涉及以下知识 ...
- Python打包分发工具setuptools
作为Python标准的打包及分发工具,setuptools可以说相当地简单易用.它会随着Python一起安装在你的机器上.你只需写一个简短的setup.py安装文件,就可以将你的Python应用打包 ...
- 第一周-JAVA基本概念
1. 本周学习总结 本周学习内容: 1.JAVA的发展 2.JDK,JVM,JRE, 3.掌握JAVA的组成结构 4.掌握使用简单的编译器写javac与java命令, 关键概念之间的联系: JVM:将 ...
- bzoj千题计划251:bzoj3672: [Noi2014]购票
http://www.lydsy.com/JudgeOnline/problem.php?id=3672 法一:线段树维护可持久化单调队列维护凸包 斜率优化DP 设dp[i] 表示i号点到根节点的最少 ...
- bzoj千题计划219:bzoj1568: [JSOI2008]Blue Mary开公司
http://www.lydsy.com/JudgeOnline/problem.php?id=1568 写多了就觉着水了... #include<cstdio> #include< ...
- JAVA_SE基础——43.抽象类
高手勿喷~ 抽象类:当定义一个类时,常常需要定义一些方法来描述该类的行为特征,但有时这些方法的实现方式是无法确定的.例如定义Animal类时,shout()方法用于表示动物的叫声,但是针对不同的动物, ...
- JAVA_SE基础——41.instanceof关键字(运算符)
instanceof 关键字 instanceof关键字的作用:判断一个对象是否属于指定的类别. instanceof关键字的使用前提:判断的对象与指定的类别必须要存在继承或者实现的关系.关于实现以后 ...
- c 语言常量
1,整数常量 整数常量可以是十进制.八进制或十六进制的常量.前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制. 整数常量也可以带一个后缀,后缀是 U 和 L 的组合 ...
- 帧动画的创建方式 - xml方式
废话不多说,先看东西 创建帧动画1 - xml方式 帧动画的创建方式主要以下2种: * 用xml创建动画: * 用代码创建动画: 本文内容主要关注 xml文件 创建帧动画的方式 xml文件 ...