《C++ 编程思想》的学习

初学《C++编程思想》,感觉很不对胃口,可能是习惯了《C++ primer》,同样是讲解相同的某个知识点,但我感觉《C++编程思想》没有primer写得好,没有primer系统、有条理、清晰好懂。但百度了下,别人说这偏重的是培养编程思想 而不是介绍具体的介绍理论  所以想了想 还是硬着头皮看下去吧!

(1)P51:这个我之前是先写了一个“1.cpp”  想将这个文件通过读写文件的方式,也就是按照“3.cpp”写的copy给另一个空的文件“2.cpp”:

结果发现不行 说main()函数重定义了  然后我将“1.cpp”的main改了 结果就可以了 这样就没有重定义可以看到本来空的文件“2.cpp”通过“3.cpp”中的命令从"1.cpp"自动copy过来的。 这样就对了   原来一个project下的几个file只能有一个main()  因为程序complile时会在整个project下搜索。 

(2)P54:我把《C++编程思想》这一页的2个小程序合在一起了 结果运行出来却只有一个结果:

我觉得应该2个结果的  后来想了想  重新为“1.cpp”创建了一个对象in2    结果是我想要的那样子了:

觉得好神奇的 为什么不能继续利用in对象呢  还要重建一个?明明都是“1.cpp”   好神奇

(3)P56:第2-7题:之前我是用char a; 然后让cin>>a,再判断if(a=='\r')原来这样不行   后来改成下面这样 可以了:

原来要用getchar()

(4)P110:我之前看c++ primer时不知道原来还有指向函数的指针 可能有讲 只是我忘记了 

现在觉得这本书也挺有意思的  是primer的加深和升华  包括P111的指向函数的指针数组 以及make分段编译。。。 

刚刚在http://bbs.csdn.net/topics/20147884看到一段话 很好 我不想评论《C++编程思想》的翻译水平如何 每讲一个知识点 回忆《C++ primer》这样学就好了  不必拘泥于翻译

这段话很好  我把对我有用的部分复制过来:

4.不要被VC、BCB、BC、MC、TC等词汇所迷惑——他们都是集成开发环境,而我们要学的是一门语言;
5.不要放过任何一个看上去很简单的小编程问题——他们往往并不那么简单,或者可以引伸出很多知识点;
6.会用Visual C++,并不说明你会C++;
7.学class并不难,template、STL、generic programming也不过如此——难的是长期坚持实践和不遗余力的博览群书;
17.C++不仅仅是支持面向对象的程序设计语言;
18.学习编程最好的方法之一就是阅读源代码;
20.请阅读《The Standard C++ Bible》(中文版:标准C++宝典),掌握C++标准;
22.别指望看第一遍书就能记住和掌握什么——请看第二遍、第三遍;
23.请看《Effective C++》和《More Effective C++》以及《Exceptional C++》;
24.不要停留在集成开发环境的摇篮上,要学会控制集成开发环境,还要学会用命令行方式处理程序;
25.和别人一起讨论有意义的C++知识点,而不是争吵XX行不行或者YY与ZZ哪个好;
26.请看《程序设计实践》,并严格的按照其要求去做;
30.读完了《Inside The C++ Object Model》以后再来认定自己是不是已经学会了C++;
31.学习编程的秘诀是:编程,编程,再编程;
32.请留意下列书籍:《C++面向对象高效编程(C++ Effective Object-Oriented Software Construction)》《面向对象软件构造(Object-Oriented Software Construction)》《设计模式(Design Patterns)》《The Art of Computer Programming》;
34.请把书上的程序例子亲手输入到电脑上实践,即使配套光盘中有源代码;
35.把在书中看到的有意义的例子扩充;
36.请重视C++中的异常处理技术,并将其切实的运用到自己的程序中;
37.经常回顾自己以前写过的程序,并尝试重写,把自己学到的新知识运用进去;
38.不要漏掉书中任何一个练习题——请全部做完并记录下解题思路;
39.C++语言和C++的集成开发环境要同时学习和掌握;
40.既然决定了学C++,就请坚持学下去,因为学习程序设计语言的目的是掌握程序设计技术,而程序设计技术是跨语言的;
41.就让C++语言的各种平台和开发环境去激烈的竞争吧,我们要以学习C++语言本身为主;
42.当你写C++程序写到一半却发现自己用的方法很拙劣时,请不要马上停手;请尽快将余下的部分粗略的完成以保证这个设计的完整性,然后分析自己的错误并重新设计和编写(参见43);
43.别心急,设计C++的class确实不容易;自己程序中的class和自己的class设计水平是在不断的编程实践中完善和发展的;
44.决不要因为程序“很小”就不遵循某些你不熟练的规则——好习惯是培养出来的,而不是一次记住的;
45.每学到一个C++难点的时候,尝试着对别人讲解这个知识点并让他理解——你能讲清楚才说明你真的理解了;
46.记录下在和别人交流时发现的自己忽视或不理解的知识点;
47.请不断的对自己写的程序提出更高的要求,哪怕你的程序版本号会变成Version 100.XX;
48.保存好你写过的所有的程序——那是你最好的积累之一;

对于这段话 我觉得写得挺好 我才刚起步 所以有些还用不到  虽然我不完完全全赞同  但会参考并将认同的部分继续实施下去  还有下面三本书:

1。More Effective c++<侯捷 译>
2.effective c++ <侯捷 译>

3.深度探索C++对象模型<侯捷 译>注:这本书要有一定C++基础的人才能看的懂。

(5)P111:指向函数的指针数组  我乍一看 觉得书上的cin.get(cr)没有用  所以我就没写  结果这个宏定义函数这里  为什么要加一个\ ?? 我试了下 不加这个会有很多错误出来  之前都没见过这样书写的  还有 现在显示出来结果也不对!!原来\是续行的意思 是为了让宏定义只相当于一行 所以要用\  真的忘记之前学过的了 哎

后来加了书上的cin.get(cr)就可以了   我现在还是不知道这句是怎么起作用的??!!我想想

  百度后 明白了 原来 cin.get()是保留空格和回车的  所以我按一个字符 再按Enter 如果用cin.get()就得用两次 哪怕第二次的没有用 因为只是为了让读进去的那个回车不影响程序 而如果用cin  就只用一个就够了  因为cin会自动丢弃空格或者回车 只读入我输入的字符  所以改成下面也是同样效果:


(6)P116:练习题

     3_4题:本来以为这个很简单 可是按'q'竟然没有跳出循环 因为我是这样:

#include<iostream>

using namespace std;
int main()
{
   char c;
   while(true)
   {
  cout<<"menu:"<<endl;
  cout<<"l:left,r:right,q:quit"<<endl;
  cin>>c;
  switch(c)
  {
  case 'q':
  break; //也许这里只用一个break
  break;
  case 'l':
  {
  cout<<"left menu:select a or b:"<<endl;
  cin>>c;
  switch(c)
  {
  case 'a':
  cout<<"you choose 'a'"<<endl;
  break;
  case 'b':
  cout<<"you choose 'b'"<<endl;
  break;
  default:
  cout<<"you didn't choose a or b."<<endl;
  }
  };
  break;
  case 'r':
  {
  cout<<"right menu:select c or d:"<<endl;
  cin>>c;
  switch(c)
  {
  case 'c':
  cout<<"you choose c"<<endl;
  break;
  case 'd':
  cout<<"you choose d"<<endl;
  break;
  default:
  cout<<"you didn't choose c or d"<<endl;
  }
  };
  break;
  default:
  cout<<"you must type l or r or q"<<endl;
  }
   }
   cout<<"quitting menu..."<<endl;
   return 0;
}

很小的一个小程序 按q竟然没跳出循环  很有意思  因为while(true)下是if里break时是可以跳出的 这个switch竟然不行  后来改成下面就行了:

原来break在while里是true且在switch里时是没办法跳出来的 之前我以为它就是不管怎么样都无条件跳出循环  有意思  之前我都不知道 果然还是太水了我

(7)今天2016.11.2最近心情不好  下班回学校后开始重新看这本书     const常量部分:

在赋值时竟然以把内建类型当成一个构造函数这样来初始化 作用同int h=7;  输出部分我以为要像后面注释那样才行 返回int 0 可是竟然不返回也可以?

书上用的是自动初始化的全0  我在想是不是只要有cout一个int型 那么即使在main快结束时 不return 0也对 上面这两个都可以 只能这样解释了吧??????!我同事说主函数main可以不返回 即使在不是void时也可以不返回      但子函数就一定要返回  因为在被调用时要用它返回的值 所以子函数必须返回

#include<string>
#include<iostream>
using namespace std;
class StringStack
{
	static const int size = 100;
	const string* stack[size];
	int index;
public:
	StringStack();
	void push(const string* s);
	const string* pop();
};
StringStack::StringStack() :index(0)
{
	memset(stack, 0, size*sizeof(string*)); //void *memset(void *s, int ch, size_t n); 将s中前n个字节替换为ch并返回指向s的指针  //新申请的内存做初始化工作
}
void StringStack::push(const string* s)
{
	if (index < size)
		stack[index++] = s;
}
const string* StringStack::pop()
{
	if (index > 0)
	{
		const string* rv = stack[--index];  
		stack[index] = 0;
		return rv;
	}
	return 0;
}
string iceCream[] = { "hello", "i am", "wang dan", "I am not", "happy", "now", "and", "you ?" };
const int iCsz = sizeof iceCream / sizeof *iceCream;
int main()
{
	/*
	int a[] = { 11, 22, 33, 44 };
	int num = sizeof a / sizeof *a;  //sizeof后面不用括号竟然也行? 这是求数组大小的
	cout << "int array size=" << num << endl;
	string aa = "I am not happy now.";
	int num2 = aa.size(); //字符串大小就不用那么麻烦的算 求 直接size就行
	cout << "string size=" << num2 << endl;
	*/
	StringStack ss;
	for (int i = 0; i < iCsz; i++)
		ss.push(&iceCream[i]);  //非const可以传给const
	const string* cp;
	while ((cp = ss.pop()) != 0)
		cout << *cp << endl;
}


如果不是指针 则const和非const之间都可以传递 没有这个约束 但指针则有



刚刚请教别人一个问题 结果别人也不知道:

#include<vector>
#include <algorithm>  
using namespace std;
struct myPoint
{
	float x;
	float y;
	bool operator<(const myPoint& pt){
		if (x == pt.x)
			return (y <= pt.y);
		return (x < pt.x);
	}
};

void main()
{
	myPoint pt1, pt2, pt3;
	pt1.x = 1;
	pt1.y = 4;
	pt2.x = 2;
	pt2.y = 0;
	pt3.x = 1.5;
	pt3.y = 4;
	vector<myPoint> vec;
	vec.push_back(pt1);
	vec.push_back(pt2);
	vec.push_back(pt3);
	//vec.push_back(pt3);
	sort(vec.begin(), vec.end());
}
这个直接运行是没有问题的  但当我把注释符号去掉 也就是说向容器vector里传递4个坐标 其中有2个相同的 然后排序  可是就会报错了:


为什么呢?当容器里的坐标都是不一样的 时候 才可以排序  一样就不可以了????后来请教另一个同事,他帮我把那个结构体修改:

struct myPoint
{
	float x;
	float y;
	bool operator<(const myPoint& pt){
		if (x == pt.x)
			return (y < pt.y);
		return (x < pt.x);
	}
};
修改成这样就行了 至于我问他为什么上面出现那个问题 他也奇怪 不知道。


今天星期四 明天双十一 我又要剁手了哈哈

明天我准备请假报驾校 就在学校不远  3080元 明天还可以领111红包  还要去体检

今天我请教别人我写的一个多个返回值的函数对不对  他说对 然后就给我科普了下函数返回值三种方式  我之前只知道两种:

#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
#include <opencv2/nonfree/features2d.hpp>
using namespace std;
using namespace cv;

float sum(float x, float y)
{
	float sum = x + y;
	return sum;
}

void func(float x, float y, float& sum, float& divide)  //引用不能少 好的编程习惯啊 好的编程习惯啊要记得
{
	if (x > 0 && y > 0)
	{
		sum = x + y+1;
		divide = x - y-1;
		return;
	}
	sum = x + y;
	divide = x - y ;
}

void main()
{
	float x = 6.0, y = 4.0;
	float sumhe = sum(x, y);
	cout << "the first result:" << sumhe << endl;
	float functionsum = 0;
	float functiondivide=0;
	func(x, y, functionsum, functiondivide);
	cout << "the second:" << functionsum << "  " << functiondivide << endl;
}
我以前熟悉的是这两种。他还告诉了我第三种 返回多个值的:

struct mytest{
	float sum;
	float divide;
};

mytest functi(float x, float y)
{
    mytest result;
	result.sum = x + y;
	result.divide = x - y;
	return result;
}

void main()
{
	float x = 6.0, y = 4.0;
	mytest myresult = functi(x, y);
	cout << "the third:" << myresult.sum<<"  "<<myresult .divide<< endl;
}
这样子也挺方便 竟然用结构体 不过这种方式可以规划到第一种  不过也可以说是另一种  毕竟自己以前不知道还可以这样用  不错不错

书上说不能在编译期间使用const值  const一般是默认内部连接也就是不分配内存

以前我也出错这样用的错  至于i[3]也就是4明明是个const int型为什么不能做数组f的size 他们说是编译器的问题  不用纠结这个  这个就记住就行了 不要这样用。

后来我同事还给我讲了堆区(需要申请,需要手动释放,malloc、new)、栈区(局部变量 出了作用域会自己释放)、静态全局数据区(static):他给我举了例子 我自己以前理解static只初始化一次的意思是这样:这个是最基本的    后来他给我举了几个例子让我说运行出来是多少  我说的和运行出来的结果不一样  他的例子是这样:我以为b的结果会是1 2 3 4结果却都是4  他讲的原因是static是全局的 一直存在的 是只有一个内存空间 f在实例化4个对象后 这四个对象里的b都是共用的那同一块内存空间 当然里面的数是一样的!!!这里令我很惊奇 我说错了!

在mian2调用结束前调用了四次析构函数 mian2执行完 b=0 所以后面又实例化又重来了

这个结果我也理解了。反正就是实例化的几个对象都共用一个static  对这个static的操作会相互影响。

然后我叫他出个题测试下我  我也想看自己是否真的理解了 然后他叫我做一个计数器 统计一个类被实例化了多少次:

#include<iostream>
using namespace std;
class f{
public:
	f(){ b++; }
	//~f(){ --b; }
	static void allnum()
	{
		cout<<"allnums:"<< b<<endl;
	}
	int geta(){ return b; }
private:
	static int b;
};
int f::b = 0;
void allf()
{
	f f1;
	int allmore = f1.geta();
	cout << "all exam:" << allmore - 1 << endl;
}
void main2()
{
	f f1;
	f f2;
}
void main3()
{
	f f1;
	main2();
	allf();
	f1.allnum();
}
void main()
{
	f::allnum();
}
我本来写了个allf()函数那样去统计 他说太绕了 要利用他讲的这个static进来  然后我又加了allnum()函数 但我是f1.allnum()那样调用的  他叫我改成了f::allnum()这样调用!的确他是对的   因为主函数中我不一定需要实例化这个类对象 我就是要直接统计 那我这样就不好  他那样是最好的 !!


他还叫我统计这个类实例还剩下多少个 我是这样写的:

上面是总共实例化了几个对象 下面是指现在还剩下多少个对象。

///


习题8-8:

因为const型指针是不可指针加指针减的。

是我自己错了 char 单引号


习题8-28:

但这个题的后三句话我没看懂??

第9章 内联函数

宏替换看来不能随便用了 除了无类型检查易致错 、还有优先级问题需要很多括弧、最后就是求值的副作用     宏定义里在三个x的地方进行了三次++a  所以出来和我们预想的不一样       看来真的不能随便用哦

#ifndef CPPTIME_H
#define CPPTIME_H
#include<ctime>
#include<cstring>
class Time{
	std::time_t t;
	std::tm local;
	char asciiRep[26];
	unsigned char lflag, aflag;
	void updateLocal(){
		if (!lflag){
			local = *std::localtime(&t);
			lflag++;
		}
	}
	void updateAscii(){
		if (!aflag){
			updateLocal();
			std::strcpy(asciiRep, std::asctime(&local));
			aflag++;
		}
	}
public:
	Time(){ mark(); }
	void mark(){
		lflag = aflag = 0;
		std::time(&t);
	}
	const char* ascii(){
		updateAscii();
		return asciiRep;
	}
	int delta(Time* dt) const{
		return int(std::difftime(t, dt->t));
	}
	int daylightSavings(){
		updateLocal();
		return local.tm_isdst;
	}
	int dayOfYear(){
		updateLocal();
		return local.tm_yday;
	}
	int dayOfWeek(){
		updateLocal();
		return local.tm_wday;
	}
	int since1990(){
		updateLocal();
		return local.tm_year;
	}
	int mouth(){
		updateLocal();
		return local.tm_mon;
	}
	int dayOfMonth(){
		updateLocal();
		return local.tm_mday;
	}
	int hour(){
		updateLocal();
		return local.tm_hour;
	}
	int minute(){
		updateLocal();
		return local.tm_min;
	}
	int second(){
		updateLocal();
		return local.tm_sec;
	}
};
#endif

void main(){
	Time start;
	for (int i = 1; i < 10000; i++){
		cout << i << ' ';
		if (i % 10 == 0)
			cout << endl;
	}
	Time end;
	cout << endl;
	cout << "start=" << start.ascii();
	cout << "end=" << end.ascii();
	cout << "delta=" << end.delta(&start) << endl;

}
好神奇 我什么时候才能把类学好 写自己的类    老板说我写的东西偏向C的风格  也是 我还是面向过程的思维  因为我以前看C++看类的时候就看不下去 那时候觉得类真的让我好累  。。。直到前几个月无意中搞深度学习用python竟然里面也讲类   发现自己竟然看得懂了  也看得下去了 所以现在才重新回头又来学C++   老板说明年如果项目的空档期可能叫我开始和他一起搞搞OpenCL、Linux等 当然还是基于算法基础上去搞  因为我不是软件部的   我只是算法部   明年如果真的开始学那些 也是为了算法的实现。。。不过多学点总是好  所以现在到明年那时候这段时间  我要把C++掌握牢  。

DEBUG(X)的确可以实现字符串拼接 但TRACE(S)并没有实现书上说的执行逗号前后的两条语句啊  ???
习题9-3:

这章讲的就是内联 我居然还想不起来用它 还改成MYBAND()和MYBAND2()宏

习题9-4:

比较简短时推荐内联 但我改成1000时 用内联反而慢:

适得其反

9-7:为什么打出的是地址呢?

请教别人后懂了而且知道了它输出是多少 那个数是怎么来的 

9-12:


9-13:


标准答案file:///C:/Users/admin/Desktop/ThinkingInC/Thinking_in_C++_Solution/html/Chap09.htm 也有错的还是我的编译器的差异

请教同事用c_str()函数就好了。另外.erase()函数我也才知道。

我开始把它错当成了类型


还有9-18、9-19叫我查看域,我没看懂它意思,原来它说的查看域就是查看类的数据成员

我同事建议我先看类,但这本书没系统的讲类,我又把《C++ primer》翻出来从第12章开始看 因为以前看了两遍但从这里都不行了,我同事说就看这个 。

于是我又开始了《C++ primer》的学习:

习题13.4:

拷贝构造函数:虽然是struct,应该和类是一样的   题目是编写一个复制构造函数复制所有成员,复制pstring指向的对象而不是复制指针。

#include<iostream>
#include<vector>
using namespace std;
struct NoName{
	NoName() :pstring(new string), i(9), d(4){} //普通构造函数
	NoName(const NoName&);//复制构造函数
	NoName& operator=(const NoName& rhs){ //赋值操作符
		if (pstring == NULL)
			pstring = new string;//其实复制构造函数中已经创建了就不用再创建了
		*pstring = *rhs.pstring;
		i = rhs.i;
		d = rhs.d;
		return *this;
	}
	~NoName(){delete pstring;}
private:
	string* pstring;
	int i;
	int d;
};
 //复制指针所指对象 这个更好理解
NoName::NoName(const NoName& no){
	pstring = new string;  //
	*pstring = *(no.pstring);
	i = no.i;
	d = no.d;
}

class Employee{
	string name;
	string* employeeid;
public:
	Employee():employeeid(new string){}
	Employee(string id){
		name = id;
		employeeid = new string;
		*employeeid = id;
	}
	//复制构造函数
	Employee(const Employee& em){
		employeeid = new string;
		name = em.name;
		*employeeid = *em.employeeid;
	}
	//赋值操作符
	Employee& operator=(const Employee& eq){
		//不需要再employeeid = new string; 拷贝构造函数中已经做了
		name = eq.name;
		*employeeid = *eq.employeeid;
		return *this;
	}
	~Employee(){delete employeeid;}
};
第二个类这个和习题答案不一样  根本用不着指针  同事说是我受上一题影响了。 语法是没错

这样就清晰的看到怎么调用的各个函数
习题13.16:

#include<iostream>
#include<vector>
#include<set>
using namespace std;
class Folder{
	set<Message*> messages;
public:
	Folder(const Folder& hi):messages(hi.messages){}
	void addMsg(Message* me){
		messages.insert(me);
	}
	void remMsg(Message* mes){
		messages.erase(mes);
	}
};
class Message{
	string contents;
	set<Folder*> folders;
	void put_Msg_in_Folders(const set<Folder*>&);
	void remove_Msg_out_Folders();
public:
	Message(const string str = "") :contents(str){}
	Message(const Message& m) :contents(m.contents), folders(m.folders){
		put_Msg_in_Folders(folders);
	}
	Message& operator=(const Message& rhs){
		if (&rhs != this){ //首先检查是否左右操作符相同
			remove_Msg_out_Folders(); //左操作数原来的指针从原folders都删掉
			contents = rhs.contents;
			folders = rhs.folders;
			put_Msg_in_Folders(rhs.folders); //右操作数的指针要多增加一个 因为左操作数
		}
		return *this;
	}
	~Message(){	remove_Msg_out_Folders();}
	void save(Folder&);
	void remove(Folder&);
};
void Message::put_Msg_in_Folders(const set<Folder*>& rhs){
	for (set<Folder*>::const_iterator beg = rhs.begin(); beg != rhs.end(); beg++)
		(*beg)->addMsg(this);
}
void Message::remove_Msg_out_Folders(){
	for (set<Folder*>::const_iterator beg = folders.begin(); beg != folders.end(); ++beg)
		(*beg)->remMsg(this); 
}
void Message::save(Folder& myfolder){
	myfolder.addMsg(this);//在这里我停顿了下 思考了是this还是contents
}
void Message::remove(Folder& m){
	m.remMsg(this);
}
13.17、13.18
#include<iostream>
#include<vector>
#include<set>
using namespace std;
class Folder{
	set<Message*> messages;
public:
	Folder(const Folder& hi):messages(hi.messages){}
	void addMsg(Message* me){
		messages.insert(me);
	}
	void remMsg(Message* mes){
		messages.erase(mes);
	}
};
class Message{
	string contents;
	set<Folder*> folders;
	void put_Msg_in_Folders(const set<Folder*>&);
	void remove_Msg_out_Folders();
	void addFldr(Folder*);
	void remFldr(Folder*);
public:
	Message(const string str = "") :contents(str){}
	Message(const Message& m) :contents(m.contents), folders(m.folders){
		put_Msg_in_Folders(folders);
	}
	Message& operator=(const Message& rhs){
		if (&rhs != this){ //首先检查是否左右操作符相同
			remove_Msg_out_Folders(); //左操作数原来的指针从原folders都删掉
			contents = rhs.contents;
			folders = rhs.folders;
			put_Msg_in_Folders(rhs.folders); //右操作数的指针要多增加一个 因为左操作数
		}
		return *this;
	}
	~Message(){	remove_Msg_out_Folders();}
	void save(Folder&);
	void remove(Folder&);
};
void Message::put_Msg_in_Folders(const set<Folder*>& rhs){
	for (set<Folder*>::const_iterator beg = rhs.begin(); beg != rhs.end(); beg++)
		(*beg)->addMsg(this);
}
void Message::remove_Msg_out_Folders(){
	for (set<Folder*>::const_iterator beg = folders.begin(); beg != folders.end(); ++beg)
		(*beg)->remMsg(this); 
}
void Message::save(Folder& myfolder){
	myfolder.addMsg(this);//在这里我停顿了下 思考了是this还是contents 结果我本来用的是contents
}
void Message::remove(Folder& m){
	m.remMsg(this);/*同事说如果用contents 那定义类干嘛 用this指向Message的指针还可以继续看这个消息在这个Folder中被删除后还存在在哪些Folder中 太妙了*/
}
void Message::addFldr(Folder* onefolder){
	folders.insert(onefolder);
}
void Message::remFldr(Folder* onefldr){
	folders.erase(onefldr);
}

另外我才知道extern是怎么用的:


函数和变量居然还不一样的!!!

13.20这就是我同事以前提醒我的一个问题  当有指针成员时一定要手写自己的拷贝构造函数、赋值操作符和析构函数!  因为这个类的每个对象都共用同一个指针 那么如果有的对象释放了这个指针而别的对象是无法知道的 依然去用这个指针 那么它是野指针!


13.19题:看了答案 才知道自己错的地方  不过答案有个错 它没有声明友元:

#include<iostream>
#include<vector>
#include<set>
using namespace std;
class Folder{
	set<Message*> messages;
	void put_Fldr_in_Messages(const set<Message*> &mes){
		for (set<Message*>::const_iterator beg = mes.begin(); beg != mes.end(); beg++)
			(*beg)->addFldr(this);
	}
	void remove_Fldr_from_Messages(){
		for (set<Message*>::const_iterator beg = messages.begin(); beg != messages.end(); beg++)
			(*beg)->remFldr(this);
	}
public:
	Folder(){}
	Folder(const Folder& hi) :messages(hi.messages){ put_Fldr_in_Messages(messages); }
	Folder& operator=(const Folder &rhs){
		if (&rhs != this){ 
			remove_Fldr_from_Messages();
			messages = rhs.messages;
			put_Fldr_in_Messages(rhs.messages);
		}
		return *this;
	}
	void addMsg(Message* me){
		messages.insert(me);
	}
	void remMsg(Message* mes){
		messages.erase(mes);
	}
	void save(Message &mymes){
		//messages.insert(&mymes); 我写的错了
		addMsg(&mymes);
		mymes.addFldr(this);
	}
	void remove(Message &mymessa){
		//messages.erase(&mymessa); 我的错
		remMsg(&mymessa);
		mymessa.remFldr(this);
	}
	~Folder(){ remove_Fldr_from_Messages(); }
};
class Message{
	friend class Folder;
	string contents;
	set<Folder*> folders;
	void put_Msg_in_Folders(const set<Folder*>& rhs){
		for (set<Folder*>::const_iterator beg = rhs.begin(); beg != rhs.end(); beg++)
			(*beg)->addMsg(this);
	}
	void remove_Msg_out_Folders(){
		for (set<Folder*>::const_iterator beg = folders.begin(); beg != folders.end(); ++beg)
			(*beg)->remMsg(this);
	}
	void addFldr(Folder* onefolder){
		folders.insert(onefolder);
	}
	void remFldr(Folder* onefldr){
		folders.erase(onefldr);
	}
public:
	Message(const string str = "") :contents(str){}
	Message(const Message& m) :contents(m.contents), folders(m.folders){
		put_Msg_in_Folders(folders);
	}
	Message& operator=(const Message& rhs){
		if (&rhs != this){ //首先检查是否左右操作符相同
			remove_Msg_out_Folders(); //左操作数原来的指针从原folders都删掉
			contents = rhs.contents;
			folders = rhs.folders;
			put_Msg_in_Folders(rhs.folders); //右操作数的指针要多增加一个 因为左操作数
		}
		return *this;
	}
	~Message(){ remove_Msg_out_Folders(); }
	void save(Folder& myfolder){
		addFldr(&myfolder);//这句我没写 之前
		myfolder.addMsg(this);//这里
	}
	void remove(Folder& m){
		remFldr(&m);//这句答案写的
		m.remMsg(this);
	}
};
这个对我来说还真复杂  我脑袋有点混沌了 。好像搞了一个地方忘记另一个地方的感觉。传说中类初期学者的感觉。

P630:

//智能指针解决类里有指针成员问题:计数类和值型类
//计数类
#include<iostream>
using namespace std;
class HasPtr{
public:
	HasPtr(int *p, int i) :ptr(new U_Ptr(p)),val(i){}
	HasPtr(const HasPtr &orig) :ptr(orig.ptr), val(orig.val){ ++ptr->use; }
	HasPtr& operator=(const HasPtr &rhs){
		++rhs.ptr->use;
		if (--ptr->use == 0)//要考虑全面 还是不容易的
			delete ptr;
		ptr = rhs.ptr;
		val = rhs.val;
		return *this;
	}
	int *get_ptr() const{ return ptr->ip; }
	int get_int() const{ return val; }
	void set_ptr(int *p){ ptr->ip = p; }
	void set_int(int i){ val = i; }
	int get_ptr_val()const { return *ptr->ip; }
	void set_ptr_val(int j)const { *ptr->ip = j; }
	~HasPtr(){ 
		if (--ptr->use==0)
			delete ptr;
	}
private:
	U_Ptr *ptr;
	int val;
};
class U_Ptr{
	friend class HasPtr;
	int *ip;
	size_t use;
	U_Ptr(int *p) :ip(p), use(1){}
	~U_Ptr(){ delete ip; }
};
//值型类 (就是手写拷贝构造函数、赋值操作符、析构函数)
class Has_ptr{
private:
	int *ptr;
	int val;
public:
	/* //自己写的错的东西
	//Has_ptr(int *p, int a) :ptr(new int(p)), val(a){} //new int()括号里装的是初始化的数 应该是ptr(new int(*p))
	Has_ptr(int *p, int a){ 
		ptr = new int;
		//ptr = p;//太蠢了 这样会内存泄漏 ptr和p都指向*p
		*ptr=*p;//这样才对
		val = a;
	}
	Has_ptr& operator=(const Has_ptr& hpt){
		if (&hpt != this){
			ptr = hpt.ptr;
			val = hpt.val;
		}
		return *this;
	}
	*/
	Has_ptr(const int &p, int i) :ptr(new int(p)), val(i){} 
	Has_ptr(const Has_ptr &org):ptr(new int(*org.ptr)),val(org.val){} //有复制构造函数还要上面的干嘛
	Has_ptr& operator=(const Has_ptr &pt){
		//ptr = pt.ptr; 傻 明明是为了解决共用一个指针的问题 还复制指针干嘛 应该复制指针所指值 这样就不是共用了
		*ptr = *pt.ptr;
		val = pt.val;
		return *this;
	}
	int get_ptr_val()const{ return *ptr; }
	int get_int() const{ return val; }
	void set_ptr(int *p){ ptr = p; }
	void set_int(int i){ val = i; }
	int *get_ptr()const{ return ptr; }
	void set_ptr_val(int p){ *ptr = p; }
	~Has_ptr(){ delete ptr; }
};

我以前以为delete了就不可以用了  原来不是那样:


原来是这样。

13.27题:a和c肯定会报错 我知道

//值型类 (就是手写拷贝构造函数、赋值操作符、析构函数)
class Has_ptr{
private:
	int *ptr;
	int val;
public:
	/* //自己写的错的东西
	//Has_ptr(int *p, int a) :ptr(new int(p)), val(a){} //new int()括号里装的是初始化的数 应该是ptr(new int(*p))
	Has_ptr(int *p, int a){ 
		ptr = new int;
		//ptr = p;//太蠢了 这样会内存泄漏 ptr和p都指向*p
		*ptr=*p;//这样才对
		val = a;
	}
	Has_ptr& operator=(const Has_ptr& hpt){
		if (&hpt != this){
			ptr = hpt.ptr;
			val = hpt.val;
		}
		return *this;
	}
	*/
	Has_ptr(const int &p, int i) :ptr(new int(p)), val(i){} 
	Has_ptr(const Has_ptr &org):ptr(new int(*org.ptr)),val(org.val){} 
	/*Has_ptr& operator=(const Has_ptr &pt){
		//ptr = pt.ptr; 傻 明明是为了解决共用一个指针的问题 还复制指针干嘛 应该复制指针所指值 这样就不是共用了
		*ptr = *pt.ptr;
		val = pt.val;
		return *this;
	}
	*/
	int get_ptr_val()const{ return *ptr; }
	int get_int() const{ return val; }
	void set_ptr(int *p){ ptr = p; }
	void set_int(int i){ val = i; }
	int *get_ptr()const{ return ptr; }
	void set_ptr_val(int p){ *ptr = p; }
	~Has_ptr(){ delete ptr; }
};
void main(){
	int obj = 45,b=23;
	Has_ptr pt(obj, 3);
	Has_ptr pt2(pt);//
	Has_ptr pt4 = pt;//直接初始化和复制初始化都调用的是拷贝构造函数
	Has_ptr pt3(b,233);
	pt3 = pt; //没有赋值操作符 这样就会报错
}
但b题怎么没报错?哦看到了  书上讲了无论有没有手写自己的析构函数  编译器总会合成一个析构函数并运行。!但没有delete掉指针 那么指针所指的对象的那块内存一直存在 会导致内存泄漏 虽然没报错 但泄漏!

另外:

//.erase()和.clear()的差别
void main(){
	vector<int*> vecpt;
	int *a = new int(12);
	int *b = new int(34);
	int *c = new int(56);
	int *d = new int(78);
	vecpt.push_back(a);
	vecpt.push_back(b);
	vecpt.push_back(c);
	vecpt.push_back(d);
	for (vector<int*>::iterator it = vecpt.begin(); it != vecpt.end(); ++it)
		delete *it; //释放容器里每个地址所指向的内容内存
	vecpt.clear(); //再把容器里每个地址清空掉 这样才算真正完成
}

看了http://blog.csdn.net/sunshine_in_moon/article/details/45457497 明白了

习题14.15

上一章学的这章就忘记了  哦多咳  。

#include<iostream>
#include<vector>
using namespace std;
class CheckoutRecord{
public:
	friend ostream& operator<<(ostream&, const CheckoutRecord&);
	friend istream& operator>>(istream&, CheckoutRecord&);
	CheckoutRecord& operator=(const CheckoutRecord &cr){
		book_id = cr.book_id;
		title = cr.title;
		borrower = cr.borrower;
		//wait_list = cr.wait_list;//学的又忘了 指针居然这样赋值 作死啊 野指针
		wait_list.clear();
		for (vector<pair<string, string>*>::const_iterator it = cr.wait_list.begin(); it != cr.wait_list.end(); ++it)
		{
			pair<string, string>* p = new pair<string, string>;
			*p = **it;
			wait_list.push_back(p);
		}
		return *this;
	}
	pair<string, string>& operator[](const size_t ind){
		//return *wait_list[ind];
		return *wait_list.at(ind); //访问容器最好用at 防止内存越界
	}
	const pair<string, string>& operator[](const size_t ind) const{
		//return *wait_list[ind];
		return *wait_list.at(ind);
	}
private:
	double book_id;
	string title;
	pair<string, string> borrower;
	vector<pair<string, string>*> wait_list;
};
ostream& operator<<(ostream &os, const CheckoutRecord &user){
	os << user.book_id << endl;
	os << (user.title).c_str() << endl;
	os << (user.borrower.first).c_str() << (user.borrower.second).c_str() << endl;
	//不能这样 原来要是const型迭代器 map好像也必须是const型迭代器
	//for (vector<pair<string, string>*>::iterator it = user.wait_list.begin; it != user.wait_list.end; ++it)
		//os << *it << " ";
	for (vector<pair<string, string>*>::const_iterator it = user.wait_list.begin(); it != user.wait_list.end(); ++it)
		os << *it << " ";
	return os;
}
istream& operator>>(istream &in, CheckoutRecord &user){
	in >> user.book_id;
	//...
	return in;
}
//.erase()和.clear()的差别
void main(){
	vector<int*> vecpt;
	int *a = new int(12);
	int *b = new int(34);
	int *c = new int(56);
	int *d = new int(78);
	vecpt.push_back(a);
	vecpt.push_back(b);
	vecpt.push_back(c);
	vecpt.push_back(d);
	for (vector<int*>::iterator it = vecpt.begin(); it != vecpt.end(); ++it)
		delete *it; //释放容器里每个地址所指向的内容内存
	vecpt.clear(); //再把容器里每个地址清空掉 这样才算真正完成
}

习题14.20、14.21:

#include <iostream>
using namespace std;
class ScreenPtr{
public:
	ScreenPtr(int j) :screenpt(new Screen(j)){}
	ScreenPtr(const ScreenPtr& sptr):screenpt(sptr.screenpt){
		++screenpt->use; //指针类型的对象就要用->操作符
	}
	ScreenPtr& operator=(const ScreenPtr& spt){
		++spt.screenpt->use;
		if (--screenpt->use==0)
			delete screenpt;
		screenpt = spt.screenpt;
		return *this;
	}
	~ScreenPtr(){ 
		if (--screenpt->use == 0)
			delete screenpt;
	 }
private:
	friend class ScreenPtrpt;
	Screen* screenpt;
};
class Screen{
private:
	friend class ScreenPtr;
	int use;
	int* truept;
	Screen(int& i) :truept(new int(i)), use(1){} 
	//Screen(int i) :truept(new int(i)), use(1){} //加上引用最好 防止了复制 性能最优
	~Screen(){ delete truept; }
};
class ScreenPtrpt{
public:
	/*  //根据需求返回 箭头操作符不一定都是返回本类有哪些成员 也可以是本类数据成员所指向类的成员等
	Screen* operator->(){
		return pt->screenpt;
	}
	const Screen* operator->()const{
		return pt->screenpt;
	}
	*/
	ScreenPtr operator->(){
		return *pt;
	}
	const ScreenPtr operator->()const{
		return *pt;
	}
private:
	ScreenPtr* pt;
};
习题14.22:

#include <iostream>
using namespace std;
class ScreenPtr{
public:
	ScreenPtr(int j) :screenpt(new Screen(j)){}
	ScreenPtr(const ScreenPtr& sptr):screenpt(sptr.screenpt){
		++screenpt->use; //指针类型的对象就要用->操作符
	}
	ScreenPtr& operator=(const ScreenPtr& spt){
		++spt.screenpt->use;
		if (--screenpt->use==0)
			delete screenpt;
		screenpt = spt.screenpt;
		return *this;
	}
	/*  //关系操作符应定义为非成员函数 又忘了!
	bool operator==(const ScreenPtr& oth){
		if (screenpt == oth.screenpt)
			return true;
		else
			return false;
	}
	bool operator!=(const ScreenPtr& othe){
		if (screenpt != othe.screenpt)
			return true;
		else
			return false;
	}
	*/
	~ScreenPtr(){ 
		if (--screenpt->use == 0)
			delete screenpt;
	 }
private:
	friend inline bool operator == (const ScreenPtr&, const ScreenPtr&);
	friend inline bool operator != (const ScreenPtr&, const ScreenPtr&);
	friend class ScreenPtrpt;
	Screen* screenpt;
};
inline bool operator == (const ScreenPtr& oth1, const ScreenPtr& oth2){
	return (oth1.screenpt == oth2.screenpt);
}
inline bool operator != (const ScreenPtr& oth1, const ScreenPtr& oth2){
	return (oth1.screenpt != oth2.screenpt);
}
class Screen{
private:
	friend class ScreenPtr;
	int use;
	int* truept;
	Screen(int& i) :truept(new int(i)), use(1){} 
	//Screen(int i) :truept(new int(i)), use(1){} //加上引用最好 防止了复制 性能最优
	~Screen(){ delete truept; }
};
class ScreenPtrpt{
public:
	/*  根据需求返回所需要的
	Screen* operator->(){
		return pt->screenpt;
	}
	const Screen* operator->()const{
		return pt->screenpt;
	}
	*/
	ScreenPtr operator->(){
		return *pt;
	}
	const ScreenPtr operator->()const{
		return *pt;
	}
private:
	ScreenPtr* pt;
};
//exercise 14.33
#include<iostream>
#include<vector>
using namespace std;
class GT_cls{
public:
	GT_cls(size_t val = 0) :bound(val){}
	//函数对象:重载了()操作符,看起来像函数调用
	bool operator()(string &s){
		return s.size() >= bound;
	}
private:
	string::size_type bound;
};
const string& func(vector<string> &vec,GT_cls g) {
	bool curr = false;
	string hasno = "no string found.";
	for (vector<string>::iterator it = vec.begin(); it != vec.end(); ++it){
	    curr = g(*it);
		if (curr == true){
			return *it;
		}
	}
	return hasno;
}
void main(){
	GT_cls g(4);
	vector<string> vec;
	string a("his");
	string b("of");
	string c("you");
	string d("hersd");
	string e("dhiahggg");
	vec.push_back(a);
	vec.push_back(b);
	vec.push_back(c);
	vec.push_back(d);
	vec.push_back(e);
	string resu=func(vec, g);
	cout << resu.c_str() << endl;
}

不对 上面这样没问题 但当我:

这样时就有问题了 我自己开始没检查出来  后来是同事帮我检查出来的!!!犯了这个作用域的错误!!!去掉引用就好了 因为我把局部变量作引用返回了  不行 。要养成习惯:返回类型通常最好要谨慎用引用  形参通常最好考虑用引用!

可是怎么别的类型的局部变量可以返回引用呢?string不行?int float等都可以返回啊 我同事说局部变量不可以返回引用?

到底string是怎么回事?同事后来帮我讲解了  反正总之 尽量不要把局部变量作引用返回!!!!!!!!!!!!!!!!!!!!!!!!

答案中是这样:输入流那里注意按Enter+Ctrl+Z+Enter  还有两次输入时 中间要cin.clear()!!!

void main(){
	vector<int> lvec;
	int ival;
	cout << "Enter strings numbers:(Ctrl+Z to end)" << endl;
	while (cin >> ival)
		lvec.push_back(ival);
	cin.clear();
	int spval;
	cout << "the size of the string you wanna:" << endl;
	cin >> spval;
	vector<int>::iterator iter;
	//find_if   <algorithm>
	iter = find_if(lvec.begin(), lvec.end(), GT_cls(spval));
	if (iter != lvec.end())
		cout << *iter << endl;
	else
		cout << "no string found!" << endl;
}

我还以为题目是第一个比指定长度长的字符串呢 。眼睛瞎了。

/

咦刚刚上CSDN来写这个错误时突然发现我自己有排名了哈哈:

纪念下哈哈。好奇怪这个排名是怎么定的。。。不管了今天2016.12.1北京时间:19.16 地点:在公司 状态:自己重新系统的学C++的类部分

/

真的要养成一个好习惯:类的声明放在头文件,类的成员函数放在一个同其头文件同名的cpp中包含那个头文件,使用测试类再另写一个cpp包含那个头文件即可!

类似这样:


第14章的习题我没全做 挑着做了一部分 而已   同事说重载操作符、函数对象这里用得相对较少。

第15章:

继承和多态的概念书上讲得太绕 同事给我讲了个例子一看就明白:

类b和类c继承于a  main函数里就是多态的解释 明明都是a1->f();但出来的结果是不一样的 因为a里面的f()函数在虚函数列表里,这个函数在new b时被b里的f()函数覆盖了 所以出来是运行b的f()  如果是new c那就是被c里面的f()函数覆盖 那么就是运行c里的f()函数  这就是多态。啊!我又忘记new了要delete!!!!

原来这也是所谓的动态绑定 比如这里的a1.f()到底调用的哪个取决于它绑定的是类b还是类c的实例化对象。

刚刚看书 看到父类的析构函数最好为virtual  我问我同事为什么 牵扯出一个有趣的现象:

他是这样解释的:当基类的析构是virtual时 派生类的析构也默认为virtual:会放在它们共用的虚析构函数列表里这个还涉及一个点就是:上面我还写得让人误解 虚函数列表前面不应该写a  因为记住:虚析构函数列表是基类和其派生类共用的!!!所以无论是派生类调用析构还是基类调用析构都是从这个虚析构函数列表从上往下运行一遍!!!!!!!!!!!!!!!!!!!!!!!!!

明白了上面的 再理解下这个:理解为什么那两条必须注释掉 不然就报错?再看下这个有意思的:是我这里说的这样 如果是我说的这样 那我就理解这个结果:a1是父类 它只调用自己的析构 c3和b3都是子类 而子类是继承自基类 可以看成里面都有父类对象 所以必定会在析构自己时去调用一次父类的析构。是我这里说的这样吧?。 但b b3(4)这个对象为什么不能显示调用析构?那编译器怎么可以调用?请教同事后懂了:

new的对象都会从虚函数列表调用析构 而且调用完不会反回虚函数列表最上面 a和b有它们共用的虚函数列表 a和c也有属于它们共用的虚函数列表 不是new的对象 就不通过虚函数列表调用析构 至于我的b3.~b();为什么报错 为什么c3.~c(); c3.~c();无论几次都不报错 是因为b里面有指针  如果我显示调用一次了 那么那个b里面成员指针p已经释放掉了 在出main时编译器会释放b3又会隐式调用一次析构 又去delete一个已经delete掉的指针  所以报错!!!!!!!!!!!!!最好的办法就是:对于指针p的释放 最好这样:

if (p){ 
delete p; 
p = NULL;
}


如果上面的父类的析构不是virtual 那么

看到15.2.4节虚函数默认实参的讲解部分:对书上的概念举了个例子

#include <iostream>
using namespace std;
class a{
public:
	a(){}
	virtual void f(int aaa=0){
		cout << "a=" <<aaa<< endl;
	}
	virtual ~a(){
		//cout << "~a()" << endl;
	}
private:
	int wd;
protected:
	int lvg;
};
class b :public a{
public:
	b() :p(new int(0)){}
	b(int i) : p(new int(i)){}
	virtual void f(char bbb='b'){
		cout << "b=" <<bbb<< endl;
	}
	~b(){ //当数据成员有指针时  要手写析构
		delete p;
		//cout << "~b()" << endl;
	}
protected:
	int *p;
};
class c :public a{ //继承
public:
	c(){}
	virtual void f(float ccc=1.222){
		cout << "c=" <<ccc<< endl;
	}
};
void main(){
	a* a0 = new a;
	a0->f();
	a* a1 = new b;
	a1->f();// 多态
	a* a2 = new c;
	a2->f(); //取决于是哪个子类实例化的指针 就调用谁的重写的函数
	delete a0;
	delete a1;
	delete a2;
	/书上概念中多态的引用或指针原来是这个意思
	//引用的情况
	//b b1;
	//c c1;
	//a& a1 = b1;
	//a& a1 = c1;
	//a1.f(); //取决于是实例化哪个子类的引用 就调用谁的重写的函数
	/*
	a* a1 = new b(5);
	a1->~a();
	a1->~a();
	delete a1;
	*/
}

结果却是改成同类型默认实参只是值不同依旧是这个结果:原本虚函数在派生类中被重写应该声明部分与基类中一样 但这个默认实参好像打破了这个机制 书上表达的是这个意思吗:

#include <iostream>
using namespace std;
class a{
public:
	a(){}
	virtual void f(int aaa=0){
		cout << "a=" <<aaa<< endl;
	}
	virtual ~a(){
		//cout << "~a()" << endl;
	}
private:
	int wd;
protected:
	int lvg;
};
class b :public a{
public:
	b() :p(new int(0)){}
	b(int i) : p(new int(i)){}
	virtual void f(char bbb='h'){
		cout << "b=" <<bbb<< endl;
	}
	~b(){ //当数据成员有指针时  要手写析构
		delete p;
		//cout << "~b()" << endl;
	}
protected:
	int *p;
};
class c :public a{ //继承
public:
	c(){}
	virtual void f(int ccc=2){
		cout << "c=" <<ccc<< endl;
	}
};
void main(){
	a* a0 = new a;
	a0->f();
	b* b1 = new b;
	b1->f();
	a* a2 = new c;
	a2->f(); 
	delete a0;
	delete b1;
	delete a2;
}
结果是 可以意义何在呢 那还不如不定义虚函数版本 直接用非虚函数不就行了?我懂了 重写虚函数必须形参类型也一样 但默认值可以不一样:

除了虚函数默认实参这个打破虚函数机制  还有另一种:

这个乍一看我没理解 既然要满足这个需要 那把基类中那个virtual不设置为virtual不就好了吗?


P732:派生类的默认构造函数、拷贝构造函数、赋值操作符、析构:

这种情况派生类没有初始化基类成员 所以会调用基类Base的合成默认构造函数 我以为应该出来是0的啊?请教同事后懂了:原来《C++ primer》上讲得太笼统了 不应该讲合成默认构造函数会初始化所有数据成员 应该讲对于类A中的是某个类类型的数据成员 A的合成默认构造函数会按这些类类型数据成员相应的默认构造函数去初始化它们 比如string是类 它会初始化为空字符串 而对于类A中的非类类型的数据成员比如int float double等 默认构造函数就不会初始化 它们保持是一些无意义的值。所以下面的就好理解了:

上面string是空字符串 后面的int是无意义的值

除了上面这个我误解的点 另外还有一个《C++ primer》上造成误解的一个点 至少是我误解了 可能我蠢。这个点就是 我记得书上说:当形参不是引用或指针类型时 发生值传递 也就是说传进来的是一个实参的副本而已 它在函数内部变成怎样都不会影响实参本身 有点像明星找替身一样 替身拍戏受伤了明星本人伤不到;而当形参是引用或指针时 不再是副本过来 而是实参本身 它在这个函数里发生什么会影响它本身 就相当于是明星拒绝替身自己上阵一样受伤了就是受伤了。我以前就是这样认为的 ,但现在看到第15章第P724页这句话:按“一旦操作符执行完毕,对象即为Item_base(基类)”按这句话的意思 那我举个例子 一个普通函数接受const的基类类型引用,那我在实参传递时,按它这句话 那我传的实参会变成基类?结果当我把派生类对象bexam传递给const基类引用后 再调用派送类自己的一个函数看能调到不 能调用到就证明这个派生类对象并没有像书上说的转成基类了 实际是调用到了 所以它并没有转成基类。我请教了同事 同事说实际书上说的是在这个test()函数体内部 当派生类对象作为实参传递给const基类引用时 它在这个函数体内部可以讲是转成了基类类型一样:所以在这里面它无法调用派生类里的函数 不然会报错。出了这个函数 它仍旧是派生类。指针和引用同理:但是这个和我以前理解的引用或指针形参不是值传递,会影响本身,所以它进去变成基类那么出来也是基类了,与我理解的这个有出入。于是他给我更深的讲解了原因:所以上面那个和下面这个就好理解了:


习题15.20:

这个题在我全部敲上去后  我没先运行  而是先自己在纸上写自己认为的结果:对比答案 我错了三个地方  也就是两个知识点的问题:

答案本来是:我自己想的对比答案是少了第8个也就是基类析构   然后在第9个和第10个之间我又多了一次基类拷贝构造函数    在第16个和第17之间之间多了一次派生类拷贝构造函数!我是这三个错  !总结起来就是两个知识点的问题:第一:iobj = func3();在调用赋值操作符给iobj以后   func3()返回的类型是一个基类类型的临时对象  它在赋值结束后会释放  而我忘记了这点!第二:将指向类类型的指针与类类型对象搞混,类类型指针Item_base* p 和Bulk_item *q并不是在创建类类型对象!类类型对象创建初始化时才可能调用拷贝构造!这个不是 它是指针 当成int*就行。

习题 15.25:

答案是错的  至少在我的编译器下是错的  我的只承认(C)是正确的。也就是只承认派生类重写基类的虚函数 返回类型 名字 形参表必须一样  默认形参可以不一样。

书上有几句话写得太绕了:和同事一起探讨才知道是这个意思:

P752:

这里有个东西:

Sales_item(const Item_base &item) :p(item.clone()), use(new size_t(1)){}
当传进来是派生类时  以前觉得传进派生类实参给基类形参 只会拿到派生类的基类部分!眼瞎 这里有个引用& 之前的认为是非引用的情况下才适用。当派生类实参传递给基类形参引用时发生的是指针型值传递:这里是引用会发生指针型值传递。即相当于Bulk_item shican;Item_base *item_shican=&shican(此时有一次动态绑定,引用也是同理。这就是指针的“值”传递,绑定到了派生类); 实际传给item的是*item_shican;所以接下来p(item.clone())这个就是运行Bulk_item的clone()。

这个非常重要!!!!!!!真正理解并记住!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

后面p757之后句柄类那里 句柄类的使用那里  我有点糊  设计到第10章的一个例子  结果那个例子有个地方是错的。。。先放下了这几页!

模板和泛型编程:

对ostringstream不是太了解  

习题16.9:

怎么硬报错说 没定义Tfind呢?

习题16.12:我也感觉没写错啊??我知道了  模板类型的容器的迭代器要重新定义为一个模板类型  但模板类型的容器不用:这样就行了!!!

但这样调用时发现问题 不行?请教老师后懂了:这种可以隐式调用

但上面的那个必须显示调用:int result = funcfrequency<int>(vecint.begin(), vecint.end());//这样显示使用的

总结就是:当函数形参里有定义的模板类型的时候 可以隐式调用  但没有的时候必须显示调用!不然编译器推测不出的。跟类模板一样要显示:

习题16.31  关于类模板:

这样也是可以的 我就这样记住好了:在类内部 当为类型时,每次用到类名后面必须跟显示指定的模板类型 !!!不是当成类型时就不用!

今天心情很差,以后我都会自己学  自己   自己   自己  自己。。。正好锻炼独立!

模板这章的习题我偷懒了的,

习题16.38:这个是非类型模板类  我是直接对着答案敲的  看完这个我准备自己写一个模板类  这样当补偿没有做这个习题好了!

#include <iostream>
using namespace std;
template<int hi,int wid>
class Screen{
public:
	typedef string::size_type index;
	friend ostream& operator<<<hi,wid>(ostream&, cosnt Screen<hi,wid>&);
	friend istream& operator>><hi, wid>(istream&, Screen<hi, wid>&);
	Screen() :contents(hi*wid, '#'), cursor(0), height(hi), width(wid){}
	Screen(const string& cont) :cursor(0), height(hi), width(wid){
		contents.assign(hi*wid, ' ');
		if (cont.size() != 0)
			contents.replace(0, cont.size(), cont);
	}
	Screen& set(char c){
		contents[cursor] = c;
		return *this;
	}
	char get() const{
		return contents[cursor];
	}
	char get(index r, index c) const{
		index row = r*width;
		return contents[row + c];
	}
	index get_cursor() const{
		return cursor;
	}
	Screen& move(index r, index c){
		if (r>=height||c>width){
			cerr << "invalid row or column" << endl;
			throw EXIT_FAILURE;
		}
		index row = r*width;
		cursor = row*c;
		return *this;
	}
	Screen& display(ostream &os){
		string::size_type index=0;
		while (index != contents.size()){
			os << contents[index];
			if ((index+1)%width==0){
				os << '\n';
			}
			++index;
		}
		return *this;
	}
	const Screen& display(ostream &os) const{
		string::size_type index = 0;
		while (index != contents.size()){
			os << contents[index];
			if ((index + 1) % width == 0){
				os << '\n';
			}
			++index;
		}
		return *this;
	}
	ostream& operator<<(ostream &os, const Screen<hi,wid> &s){
		os << "height:" << s.height << "width:" << s.width << "contents:" << s.contents;
		return os;
	}
	istream& operator>>(istream &is, Screen<hi,wid> &s){
		string cont;
		is >> s.height >> s.width >> cont;
		s.contents.assign(s.height*s.width, ' ');
		if (cont.size()!=0){
			s.contents.replace(0, cont.size(), cont);
			return is;
		}
	}
private:
	string contents;
	index cursor;
	index height, width;
};
不过我要自己写哪个模板类 我还没想好。

P831 :我是对着敲的  每次对着敲  总是好有罪恶感 可是真的对模板没太大兴趣  有点不想做这章的习题:

Queue模板类的完整版:

#include <iostream>
using namespace std;
template<class Type> class Queue;
template<class T>
ostream& operator<<(ostream &os, const Queue<T> &q){
	os << "<";
	QueueItem<T> *p;
	for (p = q.head; p; p->next)
		os << p->item << " ";
	os << ">";
	return os;
}

template<class Type>
class QueueItem{
	friend class Queue<Type>;
	friend ostream& operator<<<Type>(ostream&, const Queue<Type>&);
	QueueItem(const Type &t) :item(t), next(0){}
	Type item;
	QueueItem *next;
};

template<class Type> class Queue{
public:
	Queue() :head(0), tail(0){}
	template<class It>
	Queue(It beg, It end) : head(0), tail(0){
		copy_elems(beg, end);
	}
	Queue(const Queue &Q) :head(0), tail(0){
		copy_elems(Q);
	}
	template<class Iter> void assign(Iter beg, Iter end){
		destroy();
		copy_elems(beg, end);
	}
	Type& front(){
		return head->item;
	}
	const Type &front() const{
		return head->item;
	}
	void push(const Type &val){
		QueueItem<Type> *pt = new QueueItem<Type>(val);
		if (empty())
			head = tail = pt;
		else{
			tail->next = pt;
			tail = pt;
		}
	}
	void pop(){
		QueueItem<Type>* p = head;
		head = head->next;
		delete p;
	}
	bool empty() const{
		return head == 0;
	}
	Queue& operator=(const Queue &orig){
		destroy();
		copy_elems(orig);
		return *this;
	}
	~Queue(){
		destroy();
	}
private:
	QueueItem<Type> *head;
	QueueItem<Type> *tail;
	void destroy(){
		while (!empty())
			pop();
	}
	void copy_elems(const Queue &orig){
		for (QueueItem<Type> *pt = orig.head; pt;pt=pt->next)
			push(pt->item);
	}
	template<class Iter> void copy_elems(Iter beg, Iter end){
		while (beg != end){
			push(*beg);
			++beg;
		}
	}
	friend ostream& operator<<<Type>(ostream&, const Queue<Type>&);
};
最近开始单曲循环《告白气球》!

/**********************************************************************************************

在家里过年这11天没有搞学习 有点小内疚  本来想的是年前把这本primer后面的全部看完  结果没完成 16年就这样过去了

************************************************************************************************/

P835、泛型句柄类

//泛型句柄类 不是像原来一样专门用一个类管理使用计数
#include <iostream>
using namespace std;
template<typename T> class Handle{
public:
	Handle(T *p = 0) :ptr(p), use(new size_t(1)){}
	
	const T& operator*() const;
	T& operator*(){
		if (ptr){return *ptr;}
		throw std::runtime_error("dereference of unbound Handle");
	}
	const T* operator->() const;
	T* operator->(){
		if (ptr) return ptr;
		throw std::runtime_error("access through unbound Handle");
	}
	/
	Handle(const Handle& h) :ptr(h.ptr), use(h.use){ ++*use; }
	inline Handle<T>& operator=(const Handle &rhs){
		++*rhs.use;//防止自身赋值 
		rem_ref(); //左边的原来的要释放一次才给予新的
		ptr = rhs.ptr;
		use = rhs.use;
		return *this;
	}
	~Handle(){
		rem_ref();
	}
private:
	T* ptr;
	size_t *use;
	void rem_ref(){
		if (--*use==0)
		{
			delete ptr;
			delete use;
		}
	}
};
习题16.48:初看此题 我没懂 句柄类对象与局部对象关联是什么意思 看了答案 试着写一个时懂了:
//泛型句柄类 不是像原来一样专门用一个类管理使用计数
#include <iostream>
using namespace std;
template<typename T> class Handle{
public:
	Handle(T *p = 0) :ptr(p), use(new size_t(1)){}
	
	const T& operator*() const;
	T& operator*(){
		if (ptr){return *ptr;}
		throw std::runtime_error("dereference of unbound Handle");
	}
	const T* operator->() const;
	T* operator->(){
		if (ptr) return ptr;
		throw std::runtime_error("access through unbound Handle");
	}
	
	Handle(const Handle& h) :ptr(h.ptr), use(h.use){ ++*use; }
	inline Handle<T>& operator=(const Handle &rhs){
		++*rhs.use;//防止自身赋值 
		rem_ref(); //左边的原来的要释放一次才给予新的
		ptr = rhs.ptr;
		use = rhs.use;
		return *this;
	}
	~Handle(){
		rem_ref();
	}
private:
	T* ptr;
	size_t *use;
	void rem_ref(){
		if (--*use==0)
		{
			delete ptr;
			delete use;
		}
	}
};

void main(){
	/*
	Handle<int> hp(new int(42)); 
	Handle<int> hp2 = hp;
	cout << *hp << " " << *hp2 << endl;
	*hp2 = 10;
	cout << *hp << endl;
	*/
//句柄类与局部对象关联时的问题:局部对象撤销后 句柄类的成员指针指向的一个空 析构时delete一个野指针!!!
	for (int i = 1; i != 2;i++)
	{
		int local = 42;
		int *localargu = new int(local);//局部对象
		Handle<int> hp(localargu);
		Handle<int> hp2 = hp;
		cout << *hp << " " << *hp2 << endl;
		delete localargu;//局部对象撤销后 出这个括号时会析构Handle的两个对象时会出错
	}
}



C++编程思想源代码用于编程思想,会给大家带来帮助 Copyright (c) 2000, Bruce Eckel Source code file from the book "Thinking in C++" All rights reserved EXCEPT as allowed by the following statements: You can freely use this file for your own work (personal or commercial), including modifications and distribution in executable form only. Permission is granted to use this file in classroom situations, including its use in presentation materials, as long as the book "Thinking in C++" is cited as the source. Except in classroom situations, you cannot copy and distribute this code; instead, the sole distribution point is http://www.BruceEckel.com (and official mirror sites) where it is available for free. You cannot remove this copyright and notice. You cannot distribute modified versions of the source code in this package. You cannot use this file in printed media without the express permission of the author. Bruce Eckel makes no representation about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty of any kind, including any implied warranty of merchantability, fitness for a particular purpose, or non-infringement. The entire risk as to the quality and performance of the software is with you. Bruce Eckel and the publisher shall not be liable for any damages suffered by you or any third party as a result of using or distributing this software. In no event will Bruce Eckel or the publisher be liable for any lost revenue, profit, or data, or for direct, indirect, special, consequential, incidental, or punitive damages, however caused and regardless of the theory of liability, arising out of the use of or inability to use software, even if Bruce Eckel and the publisher have been advised of the possibility of such damages. Should the software prove defective, you assume the cost of all necessary servicing, repair, or correction. If you think you've found an error, please submit the correction using the form you will find at www.BruceEckel.com. (Please use the same form for non-code errors found in the book.)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

元气少女缘结神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值