土曜日, 8月 03, 2013

プチクイズ8 - コピーコンストラクタ

Q. 以下のコードのコンパイル&実行結果は?

#include <cstdio>
#include <cstdlib>
#include <string>

class C {
private:
 int d;
public:
 C() { d = 5;};
 ~C() {};
 C(C m) { d = m.d; }
 virtual void a() { printf("aaa"); }
 int GetD() { return d; }
};

int main() {

 C m;
 C m2(m);
 
 printf("%d\n",m2.GetD());
}

A.
大抵のコンパイラではコンパイルできないはず.上記コピーコンストラクタは値渡しのパラメータが定義されているので,このコピーコンストラクタが呼ばれ,ランタイム上で値渡しのパラメータmを”コピー”しようとコピーコンストラクタを更に呼び... と永遠に再帰呼び出しが発生する.
この問題を避けるには,const C&にパラメータの宣言を変更すればよい.

プチクイズ 7 - クラスのsizeof

Q1. 空のクラスに対してのsizeofの結果は?

A1. (visual studioでもgnu c++でも)1.実質データは0だがクラスの存在を示すためにダミーのデータ(size 1)が作られる.

Q2. コンストラクタとデストラクタを宣言した場合?

A2. 変わらず.ただしvirtualであればvtableを作るのでその分のサイズは増える.32bitマシン上なら4, 64bitマシン上なら8.

Q3. 関数を宣言すると?

A3. コンストラクタ・デストラクタに同じ.virtualであればvtable分の増加,さもなければ何も変わらず.

Q4. intの変数をクラスに追加すると?

A4. int型分のサイズが増加.

アルゴリズム問題を解く 13 - 回文

問題

Please implement a function that checks whether a positive number is a palindrome or not.
For example, 121 is a palindrome, but 123 is not. Please write an answer in C.
( Coding interviewsより抜粋 )

ヒント

山本山,とかたけやぶやけた,に共通するのは,文字列長が奇数であることである.この問題はマイナスでない整数であることも忘れずに.

答え1

先頭と最後尾,先頭+1と最後尾-1..を比較していけばよいのでO(N/2)の計算量でよい.
NULLの場合,負の場合等々といったチェックも忘れずに.

#include <cstdio>
#include <cstdlib>
#include <string>

int isdigits(const char* const s, int len) {

 int result = 1;
 for( int i = 0;i < len;i++ ) {
  if( !isdigit(s[i]) ) {
   result = 0;
   break;
  }
 }

 return result;

}

int ispalindrome(const char* const s, unsigned int len) {

 // prerequisite check
 if( s[0] == '-' ||
  !(len % 2) ||
  len == 1 ||
  s == NULL ||
  !isdigits(s,len))
  return 0;
  
 int result = 1;
 unsigned int half = len >> 1;
 for( unsigned int i = 0;i < half;i++ ) {
  if(s[i] != s[len-1-i]) {
   result = 0;
   break;
  }
 }
 return result;
}

int main() {

 char* s1 = "142";
 char* s2 = "5";
 char* s3 = "282";
 char* s4 = "2820";
 char* s5 = "-10";
 char* s6 = "ABA";
 printf("%d\n",ispalindrome(s1,strlen(s1)));
 printf("%d\n",ispalindrome(s2,strlen(s2)));
 printf("%d\n",ispalindrome(s3,strlen(s3)));
 printf("%d\n",ispalindrome(s4,strlen(s4)));
 printf("%d\n",ispalindrome(s5,strlen(s5)));
 printf("%d\n",ispalindrome(s6,strlen(s6)));

 char ch;
 scanf(&ch);
}

答え2

答え1よりはるかに優れたコードである.

#include <cstdio>
#include <cstdlib>
#include <string>

int ispalindrome(unsigned int n) {

 if( n <= 110 )
  return 0;

 unsigned int orig = n;
 unsigned int rev = 0;
 while( n != 0 ) {
  rev = rev * 10 + n % 10;
  n /= 10;
 }

 return (rev == orig) ? 1 : 0;

}

int main() {

 unsigned int s1 = 142;
 unsigned int s2 = 5;
 unsigned int s3 = 282;
 unsigned int s4 = 2820;
 unsigned int s5 = 111;

 printf("%d\n",ispalindrome(s1));
 printf("%d\n",ispalindrome(s2));
 printf("%d\n",ispalindrome(s3));
 printf("%d\n",ispalindrome(s4));
 printf("%d\n",ispalindrome(s5));

}

金曜日, 8月 02, 2013

constの使い方

面接をしていて感じたのが,意外にconstの使い方をわかっていない又は誤って理解しているケースも多いということである.

constの正しい用法はcode as documentationとして重要なのでしっかり理解しておきたいところ.

int i = 500;
// 1. 
const int* pVal = &i;
// 2.
int* const pVal = &i;
// 3.
const int* const pVal = &i;

1はconst修飾子が適用されたint型,つまり定数intを指すポインタである.
ゆえにポインタを経由してpValの値を変更することはコンパイラが許可しないが,pVal自体別のconst int型を指し示すことはできる.

2はint型を示すconst修飾子が適用されたポインタ,言い換えるとそのポインタの持つ値,つまりはアドレスの変更が不可能なポインタである.ポインタ経由でiの値を変更することは認めるが,i意外の変数を指し示すことは許さない.

3は定数int型を示す定数ポインタである.ポインタ経由での値の変更も許されなければ,またポインタの指す値を変えることも出来ない.